Compare commits

...

439 commits

Author SHA1 Message Date
Tim-Philipp Müller a84bbc66f3 Update versions to 0.9.13
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1459>
2024-02-12 19:20:50 +00:00
Sebastian Dröge d0ea7587d2 Update GStreamer bindings in Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:07:07 +02:00
Sebastian Dröge 289fdaf490 gtk4: Always draw a black background behind the video frame
This makes sure that there is the same background behind the frame, no
matter if black borders have to be added or not. Without this a frame
that has transparency would change rendering depending on the layout.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:07:07 +02:00
Ruben Gonzalez ea93b8896d gtk4: Fix segfault running gst-inspect -a when GTK4 and GTK3 is installed
Segmentation fault when getting default value of paintable property
from gtk4paintablesink element when libgtk-4.so.1 from libgstgtk4.so
and libgtk-3.so.0 from libgstgtk.so are installed:

> cannot register existing type 'GdkDisplayManager'

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/490

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:07:07 +02:00
Mathieu Duponchelle 32cb2877fd textwrap: add support for gaps
When accumulate-time is non-zero, we need to drain our accumulated
text once the threshold is reached.

Implement support for gaps the simplest way, by transforming it into
an empty buffer and chaining it through ourself.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:07:07 +02:00
Michael Tretter 2d796e5108 livesync: add support for image formats
The livesync element is also useful for Motion JPEG streams. However,
Motion JPEG uses image/ caps instead of video/ caps.

The framerate is defined for image/, too.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
Michael Tretter 0f1fde5d0a meson: allow building plugins with GTK 4 examples
Only the examples of the fallbackswitch, livesync, and togglerecord
plugins require the gtk, gio, and gst-plugin-gtk4 features. The plugins
themselves don't actually have a dependency on GTK.

Only add the features (and examples) if the examples are actually
enabled to allow building these plugins without the GTK dependency.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
Nirbheek Chauhan 43b9aab88e meson: Add nasm to PATH if meson can find it
Fixes rav1e build on Windows when built inside the monorepo.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
Nirbheek Chauhan de64699eaa meson: pkg-config is required at build time
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
Nirbheek Chauhan e782fd14c2 meson: Fix build on Windows with MSVC
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/480

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
Michael Tretter 3bc6f7c614 meson: remove trailing whitespace and add comma
Cleanup without functional change.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
Guillaume Desmottes 90342ff90e livesync: properly format jitter in debug logs
Easier to read that way.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 19:01:42 +02:00
François Laignel 0e9d33b38b webrtc: signallers: attempt to close the ws when an error occurs
This commit discards the early error returns in the send tasks to log the error
and attempt to close the websocket.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 17:50:52 +01:00
François Laignel 231389a990 webrtc: only use close() to close websockets
In the signaller clients and servers, the following sequence is used to close
the websocket (in the [send task]):

```rust
    ws_sink.send(WsMessage::Close(None)).await?;
    ws_sink.close().await?;
```

tungstenite's [`WebSocket::close()` doc] states:

> Calling this function is the same as calling `write(Message::Close(..))``

So we might think they are redundant and either could be used for this purpose
(`send()` calls `write()`, then `flush()`).

The result is actually is bit different as `write()` starts by checking the
state of the connection and [returns `SendAfterClosing`] if the socket is no
longer active, which is the case when a closing request has been received from
the peer via a [call to `do_close()`]). Note that `do_close()` also enqueues a
`Close` frame.

This behaviour is visible from the server's logs:

```
1. tungstenite::protocol: Received close frame: None
2. tungstenite::protocol: Replying to close with Frame { header: FrameHeader { .., opcode: Control(Close), .. }, payload: [] }
3. gst_plugin_webrtc_signalling::server: Received message Ok(Close(None))
4. gst_plugin_webrtc_signalling::server: connection closed: None this_id=cb13892f-b4d5-4d59-95e2-b3873a7bd319
5. remove_peer{peer_id="cb13892f-b4d5-4d59-95e2-b3873a7bd319"}: gst_plugin_webrtc_signalling::server: close time.busy=285µs time.idle=55.5µs
6. async_tungstenite: websocket start_send error: WebSocket protocol error: Sending after closing is not allowed
```

1: The server's websocket receives the peer's `Close(None)`.
2: `do_close()` enqueues a `Close` frame.
3: The incoming `Close(None)` is handled by the server.
4 & 5: perform session closing.
6: `ws_sink.send(WsMessage::Close(None))` attempts to `write()` while the ws
   is no longer active. The error causes an early return, which means that
   the enqueued `Close` frame is not flushed.

Depending on the peer's shutdown sequence, this can result in the following
error, which can bubble up as a `Message` on the application's bus:

```
ERROR: from element /GstPipeline:pipeline0/GstWebRTCSrc:webrtcsrc0: GStreamer encountered a general stream error.
Additional debug info:
net/webrtc/src/webrtcsrc/imp.rs(625): gstrswebrtc::webrtcsrc:👿:BaseWebRTCSrc::connect_signaller::{{closure}}::{{closure}} (): /GstPipeline:pipeline0/GstWebRTCSrc:webrtcsrc0:
Signalling error: Error receiving: WebSocket protocol error: Connection reset without closing handshake
```

On the other hand, [`close()` ensures the ws is active] before attempting to
write a `Close` frame. If it's not, it only flushes the stream.

Thus, when we want to be able to close the websocket and/or to honor the closing
handshake in response to the peer `Close` message, the `ws_sink.close()`
variant is preferable.

This can be verified in the resulting server's logs:

```
tungstenite::protocol: Received close frame: None
tungstenite::protocol: Replying to close with Frame { header: FrameHeader { is_final: true, rsv1: false, rsv2: false, rsv3: false, opcode: Control(Close), mask: None}, payload: [] }
gst_plugin_webrtc_signalling::server: Received message Ok(Close(None))
gst_plugin_webrtc_signalling::server: connection closed: None this_id=192ed7ff-3b9d-45c5-be66-872cbe67d190
remove_peer{peer_id="192ed7ff-3b9d-45c5-be66-872cbe67d190"}: gst_plugin_webrtc_signalling::server: close time.busy=22.7µs time.idle=37.4µs
tungstenite::protocol: Sending pong/close
```

We now get the notification `Sending pong/close` (the closing handshake) instead
of `websocket start_send error` from step 6 with previous variant.

The `Connection reset without closing handshake` was not observed after this
change.

[send task]: 63b568f4a0/net/webrtc/signalling/src/server/mod.rs (L165)
[`WebSocket::close()` doc]: https://docs.rs/tungstenite/0.21.0/tungstenite/protocol/struct.WebSocket.html#method.close
[returns `SendAfterClosing`]: 85463b264e/src/protocol/mod.rs (L437)
[call to `do_close()`]: 85463b264e/src/protocol/mod.rs (L601)
[`close()` ensures the ws is active]: 85463b264e/src/protocol/mod.rs (L531)

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 17:50:24 +01:00
Nirbheek Chauhan 3cfb28d048 webrtc/signalling: We get the address when accepting
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 16:56:02 +02:00
Nirbheek Chauhan e3190c888a webrtc/signalling: Fix potential hang and FD leak
If a peer connects via TCP and never initiates TLS, then the server
will get stuck in the accept loop. Spawn a task when accepting a TLS
connection, and timeout if it doesn't complete in 5 seconds.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1456>
2024-02-12 16:53:06 +02:00
Sebastian Dröge c03e108487 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:42:48 +02:00
Arun Raghavan 7bb277884b threadshare: Fix a deadlock in used-socket notification
This manifests in a gst-launch-1.0 pipeline using ts-udpsrc, since
notification of used-socket results in the property being read by the
application, and the settings lock causes a deadlock.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:27:16 +02:00
Arun Raghavan 9d802b1969 threadshare: Fix a typo while logging
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:27:16 +02:00
Mathieu Duponchelle 630a1120ba webrtcsink: don't panic on failure to request pad from webrtcbin
webrtcbin will refuse pad requests for all sorts of reasons, and should
be logging an error when doing so, simply post an error message and let
the application deal with it, the reason for the refusal should
hopefully be available in the logs to the user.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:27:16 +02:00
Seungha Yang 55d2b9483c fallbacksrc: Fix timeout scheduling
Other thread can schedule the timeout (e.g., unblock signal
or active pad change) while state lock is released

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:25:50 +02:00
Sebastian Dröge 2136befbef ndi: Don't mark private type as public
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:24:32 +02:00
Sebastian Dröge 11ea962f81 ndi: Remove wrong Clone impl on RecvInstance
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1411>
2023-12-18 10:24:32 +02:00
Tim-Philipp Müller b81b2cee81 net: aws: tests: use unicode escapes for unicode characters
.. instead of putting actual multi-byte unicode characters into
the source code which appears to make hotdoc unhappy.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1390>
2023-11-13 12:58:25 +00:00
Sebastian Dröge ee3a272859 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Sebastian Dröge f5d633d293 Update versions to 0.9.12
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Sebastian Dröge 4e05fc5999 sccparse: Fix leading spaces between the tab and caption data
CCExtractor is creating files like this.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Sebastian Dröge eff3618ebd Set sync=false in rsfilesink / s3sink
BaseSink defaults to sync=true and that doesn't make much sense for
these elements.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 9ffcb14617 livesync: Remove the stop from outgoing segments
Our buffer duplication can extend a segment indefinitely.

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/452
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 8114f94f73 livesync: Keep existing buffer duration in some cases
Resize a repeat buffer only if caps gave us a duration to use, or we
consider its current duration unreasonable.

In particular, for audio streams we should prefer reusing the buffer
size upstream gave us, as we did before 6633cc4046.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 72506b94e3 livesync: Split fallback_duration into in_ and out_duration
Make it independent of the `latency`; this was inconsistent anyway,
where the default latency of zero got you a fallback duration of 100 ms
and something else got you half the latency.

Maintain a separate duration for the `in` and the `out` side so we
change the duration of repeat buffers after a caps change, not just
before.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Guillaume Desmottes dafdb48ff6 livesync: display jitter when waiting on clock
We already log the result of the clock wait call so may as well log the
returned jitter.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Guillaume Desmottes 4f9d5b7a79 livesync: log new pending segments
The debug print of the event does not display details about the segment:
  Unqueueing Some(Event(Event { ptr: 0x7fa3e0002580, type: "segment", seqnum: Seqnum(479), structure: Some(GstEventSegment { segment: (GstSegment) ((GstSegment*) 0x7fa3e8001d00) }) }))

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 1eaac4e0a2 livesync: example: Add identities single-segment=1
These let us change the runtime offset of the test buffers via pad
offsets without pushing new segments into livesync, which is necessary
to demo the late-threshold behavior.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) b8e891b5b7 livesync: Use fallback_duration for audio repeat buffers as well
Don't depend on upstream giving us sanely-sized buffers if we want to
repeat.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 474f00f8d6 livesync: Separate out_buffer duplicate status from GAP flag
Otherwise we might get confused by upstream GAP buffers.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 98d839a920 livesync: Handle flags and late buffer patching after queueing
This makes the chain function almost independent of the output state. We
still do the early discard check with `buffer_is_backwards` so we don't
try to queue buffers we can't use, allowing us to fast-forward upstream
without blocking on the src task.

Don't accept `LateOverThreshold` buffers when we have `pending_caps` or
a `pending_segment`. We need to apply these first before we can sensibly
patch buffers from the new stream.

Deduplicate most of the output buffer patching code into a new
`patch_output_buffer` method.

For: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/450
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) f565875b6c livesync: Simplify num_duplicate counting
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 873b91d7d5 livesync: Move num_in counting to the src task
This is in preparation for moving more accept/discard logic to the src
task, so we can only count `num_in` here.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 395ef40fc9 livesync: Move a notify closer to the interesting state change
Move the `notify_all` to where we pop the buffer. We're moving within a
single state lock so no change in behavior.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) ad97b21011 livesync: Replace an if-let with match
No change in behavior, yet. Separate commit to ease reviewing.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) fa05a305b6 livesync: Clean up state handling
- Separate resetting state more cleanly, introducing `set_flushing`,
  `sink_reset` and `src_reset`.
- Clear the queue early when we flush, in order to unblock waits on
  query responses.
- Return an error when we fail to start, pause or stop the task.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 274e3cd71b livesync: Log a category error when we are missing the segment
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) ab84da6d32 livesync: Improve audio duration fixups
- An entirely missing duration is now only logged at debug level instead
  of pretending the duration was zero and warning about it.
- Silently fix up a duration difference up to one sample.
- Error when we fail to calculate the duration; don't try to apply the
  `fallback_duration` to a non-video stream.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 7a70863152 livesync: Simplify start_src_task and src_loop
This should effect no change in behavior.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Jan Alexander Steffens (heftig) 4c4aff3a5b livesync: Rename activatemode methods to *_activatemode
This matches the other plugins.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Arun Raghavan d35fc2eb6d aws: s3: Properly percent-decode GstS3Url
We previously only percent-decoded the first fragment. This doesn't
necessarily harm anything, but for consistency we keep the structure
un-encoded, and encode when converting to a string representation.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Arun Raghavan 8f97d691e1 aws: s3sink: Fix handling of special characters in key
Properly URL-encode the string if needed, and add some tests for a
couple of cases.

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/431
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Sebastian Dröge 7d2e849bbe rtpav1depay: Don't push stale temporal delimiters downstream
Only push them downstream once a complete OBU was assembled.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Sebastian Dröge 7bb6f0265c rtpav1depay: Skip unexpected leading fragments
If a packet is starting with a leading fragment but we do not expect to
receive one, then skip over it to the next OBU.

Not doing so would cause parsing of the middle of an OBU, which would
most likely fail and cause unnecessary warning messages about a
corrupted stream.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Lieven Paulissen beb524e950 ndisrc: Assume input with more than 8 raw audio channels is unpositioned
gst_audio_channel_positions_from_mask() will otherwise print warnings
all the time.

Fixes #444

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Stéphane Cerveau ac3ca76c9f fmp4mux: specify the fragment duration unit
The fragment duration is expressed in nanoseconds.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
François Laignel 3627e52673 net/webrtcsink: don't miss ice candidates
During `on_remote_description_set()` processing, current session is removed
from the sessions `HashMap`. If an ice candidate is submitted to `handle_ice()`
by that time, the session can't be found and the candidate is ignored.

This commit wraps the Session in the sessions `HashMap` so an entry is kept
while `on_remote_description_set()` is running. Incoming candidates received by
`handle_ice()` will be processed immediately or enqueued and handled when the
session is restored by `on_remote_description_set()`.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:47:41 +02:00
Maksym Khomenko 19597b3737 webrtcsrc: add turn-servers property
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:46:28 +02:00
Maksym Khomenko 8355f93f5f webrtcsrc: use @watch instead of @to-owned
@to-owned increases refcount of the element, which prevents the object from proper destruction, as the initial refcount with ElementFactory::make is larger than 1.

Instead, use @watch to create a weak reference and unbind the closure automatically if the object gets destroyed

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Piotr Brzeziński ecabf02b1b webrtc: Fix paths in README
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Sean DuBois 2aacc74a44 net: webrtc/webrtchttp: Respect HTTP redirects
Properly follow redirect URL. Before new request would be made, but with
original URL again.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Seungha Yang d2e5cb43cc hlssink3: Various cleanup
* Simplify state/playlist management
* Fix a bug that segment is not deleted if location contains directory
and playlist-root is unset
* Split playlist update routine into two steps, adding segment
to playlist and playlist write

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Seungha Yang 29cbfbf970 hlssink3: Don't remove uri from playlist if playlist-length is zero
Behave as documented in property description

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Seungha Yang 9e46f6c411 hlssink3: Don't remove old files if max-files is zero
Follow hlssink2 element's behavior

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Seungha Yang 98b104dc7b hlssink3: Remove unused deps
gstreamer-base dep is unused. And use gst::glib

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Seungha Yang fe333063ab hlssink3: Use sprintf for segment name formatting
The zero-padded naming requirement is unnecessary. Use simple
sprintf instead

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 17:32:39 +02:00
Seungha Yang 0bb381aad9 hlssink3: Use Path API for getting file name
Current implementation does not support Windows path separator.
Use Path API instead.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1387>
2023-11-10 14:28:42 +02:00
Sebastian Dröge 32a730f6fa fmp4mux: Don't overflow negative composition offset calculation
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1338>
2023-09-27 20:28:03 +03:00
Guillaume Desmottes 1d52139e35 fallbackswitch: protect src pad stream lock using Cond
Should prevent stream and State deadlocks, see https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/202

Fix #202
Hopefully fix #192 as well.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1329>
2023-09-20 19:45:00 +03:00
Guillaume Desmottes c9c49dc54c fallbackswitch: prevent deadlocks in chain function
Calling schedule_timeout() may result in handle_timeout() being called right away,
which will need pad state locks which was still hold in chain().

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1329>
2023-09-20 19:44:52 +03:00
Guillaume Desmottes 2e4135ceff fallbackswitch: ensure strict ordering when taking mutexes
Should prevent deadlocks.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1329>
2023-09-20 19:44:44 +03:00
Sebastian Dröge 755f021a30 gtk4: Only support RGBA/RGB in the GL code path
For all other component orderings a shader is necessary to re-order the
components for what GTK expects.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1328>
2023-09-20 13:52:34 +03:00
Ivan Molodetskikh 4da3bbf1d3 gtk4: Premultiply alpha in GL textures
GTK expects GL textures to have premultiplied alpha. The ones we get
from GStreamer don't, leading to incorrect rendering of semitransparent
frames.

GTK 4.12 gained an API to set a different GL texture format, but it
won't help for older GTK versions. Plus, at the time of writing, it
causes a very slow download/upload path in GTK.

So, use a GTK GL shader node to premultiply the alpha without leaving
the GPU.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1328>
2023-09-20 13:52:34 +03:00
Sebastian Dröge c261dd4445 webrtcsink: Fix clippy warning
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 11:50:27 +03:00
Sebastian Dröge 2a504bbf17 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 11:50:27 +03:00
Sebastian Dröge 04136b6cc1 onvifmetadataparse: Skip metadata frames with unrepresentable UTC time
Previously we would panic, which causes the element to post an error
message. Instead, simply skip metadata frames if their UTC time since
the UNIX epoch can't be represented as nanoseconds in u64.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 11:50:27 +03:00
Seungha Yang 4fc905c9ea webrtcsink: Propagate GstContext messages
Implement CustomBusStream so that NEED_CONTEXT and HAVE_CONTEXT
messages from session/discovery can be forwarded to parent
pipeline and also GstContext can be shared.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 11:47:34 +03:00
Seungha Yang 581787f651 webrtcsink: Add support for d3d11 memory and qsvh264enc
Adding d3d11 memory and qsvh264enc support

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 11:45:05 +03:00
Robert Ayrapetyan 2151df4d70 webrtcsink: fix TWCC extension adding
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:22:45 +03:00
robert 94ab108069 meson: Fix handling of optional deps
We were requiring the presence of all optional dependencies, such as
gstreamer-check-1.0 and gstreamer-gl-1.0, on the system, regardless of
whether the user actually requires these functionalities.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:15:48 +03:00
L. E. Segovia e92084885c meson: Tell cargo to prefer static libraries
This fixes most, but not all, of the build errors in Windows when using
static libraries.

The ones remaining are:

- redirection of gstreamer-1.0 towards gstreamer-full-1.0
- Cairo not exporting the C++ stdlib requirement when built statically

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:15:22 +03:00
L. E. Segovia 390e7ff47a meson: Disable plugins and related outputs if features are disabled
Previously, there was no check performed on features of plugins if these
specify GStreamer plugins. This commit adds that, and ensures that the
plugins and pkg-config targets are skipped if no outputs are to be
generated (this is already done for examples).

Closes #369

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:15:09 +03:00
L. E. Segovia 31ad311d7f meson: Disable plugins and related outputs if features are disabled
Previously, there was no check performed on features of plugins if these
specify GStreamer plugins. This commit adds that, and ensures that the
plugins and pkg-config targets are skipped if no outputs are to be
generated (this is already done for examples).

Closes #369

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:15:03 +03:00
L. E. Segovia d6f42a2037 meson: Allow usage of externally overridden pkg-config
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:14:58 +03:00
Nirbheek Chauhan 379d3de31c meson: Don't require Python 3.8 for cargo_wrapper.py
Documentation on gstreamer monorepo is disabled because the image we
use to build the docs only has Python 3.7

https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2873

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:00:38 +03:00
Nirbheek Chauhan aa4ba44794 meson: Don't override RUSTFLAGS in the env
Meson does not add RUSTFLAGS to rustc.cmd_array(), so the effect of
this code is to override that value in the env.

This is hacky, since the env has to match during setup and compile
now, and rust_args added in the cross / native file will be ignored.
But at least it fixes this regression:

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/372

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 10:00:33 +03:00
Mathieu Duponchelle 1ccc93df07 webrtcsink: don't forget to setup encoders for discoveries
The "encoder-setup" signal must also be emitted for the encoders
used in discovery pipelines in order for the default settings to
be applied.

This otherwise meant that for instance the x264 encoder would
use a 60 frames latency, greatly delaying startup.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 09:59:17 +03:00
Seungha Yang 257a0de483 transcriberbin: Configure audioresample in front of transcriber
Allows any samplerate and make it negotiable. Fixing a scenario
where transcriberbin is configured with passthrough enabled,
(and negotiated samplerate is not supported by transcriber)
and then setting passthrough=false later during playback.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 09:43:43 +03:00
Sebastian Dröge 17f7b04b82 webrtcsink: NVIDIA V4L2 encoders always require NVMM memory
And if the input is not like that then a corresponding converter must be
inserted.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1327>
2023-09-20 09:40:58 +03:00
Sebastian Dröge 0205aa43d6 Udpate Cargo.lock 2023-07-20 15:30:30 +03:00
Sebastian Dröge 36cdf84655 Update version to 0.9.11 2023-07-20 15:15:07 +03:00
Sebastian Dröge 38cfc72a72 ci: Remove omx=disabled from the documentation job
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:53:53 +03:00
Sebastian Dröge 96b595d942 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:47:25 +03:00
Sebastian Dröge b6168c7255 fmp4mux: Fix draining in chunk mode if keyframes are too late
We would create another chunk that ends after the fragment end, and
would from then on consider the stream always filled for the chunk
because it starts after the current fragment end (i.e. nothing would go
into this fragment).

This is obviously wrong because the actual fragment end moved further
ahead because of the additional chunk.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:41:58 +03:00
Mathieu Duponchelle b4b2ca9a82 webrtcsink: fix pipeline when input caps contain max-framerate
GstVideoInfo uses max-framerate to compute its fps, but this leads
to issues in videorate when framerate is actually 0/1.

Fix this by stripping away max-framerate from input caps

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:41:43 +03:00
Sebastian Dröge fc75502ee4 webrtcsink: Configure only 4 threads for x264enc
More threads can cause more slices to be created, and Chrome simply falls
apart if there are more than a few slices and fails decoding.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:38:44 +03:00
Sebastian Dröge 9854b299a2 webrtcsink: Translate force-keyunit events to force-IDR action signal for NVIDIA encoders
NVIDIA's v4l2 encoder elements don't handle the force-keyunit events but
instead provide a custom action signal based API for requesting a
keyframe.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:38:28 +03:00
Sebastian Dröge bdcdbfeaaf webrtcsink: Set config-interval=-1 and aggregate-mode=zero-latency on rtph26[45]pay
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:38:18 +03:00
Sebastian Dröge 482b7e1469 webrtcsink: Set VP8/VP9 payloader based on payloader element factory name
Instead of checking the encoder's name. There are more VP8/VP9 encoders
than the ones from the vpx plugin.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:38:14 +03:00
Jan Schmidt eda38a637e fallbackswitch: Change the threshold for trailing buffers
Only discard buffers on inactive pads if they are later
than the current output running time, rather than the
later timeout running time. That can mean switching
to a higher priority pad can happen quicker.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:37:46 +03:00
Jan Schmidt 081a90fefc fallbackswitch: Fix pad health calculation and notifies
Change the pad health calculation to consider a pad 'healthy'
if it has received data within the last 'timeout' window. Previously,
inactive pads were constantly flip-flopping between healthy and not
healthy depending on whether they were slightly ahead of or behind
the active pad running_time.

When the health status of a pad changes, make sure to always notify
the property, so that applications that are manually controlling
the active pad can make their switching decisions.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1285>
2023-07-19 09:37:38 +03:00
Sebastian Dröge 4b0330f680 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:46:46 +03:00
yatinmaan 6eaeb2e127 gtk4: Add python example
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:41:21 +03:00
Vivia Nikolaidou 774dda249e togglerecord: Clip segment before calculating timestamp/duration
Clipping happens in buffer time space and data.clip can modify the
buffer timestamp and duration. Move it before we calculate them in order
to make it actually have some effect.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:41:21 +03:00
Vivia Nikolaidou 01f96ca40c togglerecord: Error out if main stream buffer has no valid running time
We cannot continue with this buffer, because we cannot calculate the
time when the recording stopped or started. We also cannot safely drop
it, because that might break the stream, especially if it's encoded.
Therefore, we return an element error.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:41:21 +03:00
Sebastian Dröge fafe52475f webrtcink: Use correct property types for nvvideoconvert
These are enums and not plain integers.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:41:21 +03:00
Sebastian Dröge 8fc469b8e0 videofx: Minimize dependencies of the image crate
Only the basic infrastructure is needed and none of the
decoders/encoders for various image formats.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:41:21 +03:00
Sebastian Dröge c74def0b12 livesync: Wait for the end timestamp of the previous buffer before looking at queue
Previously livesync was waiting for the start timestamp of the current
buffer after looking at the queue and right before pushing it
downstream. This meant that it generally looked too early at the queue
and especially that upstream had to provide the next buffer already at
the start timestamp of the previous one.

Instead, now wait before looking at the queue and wait for the end
timestamp of the previous buffer. Once the previous buffer has expired,
a new buffer really needs to be available or otherwise a filler buffer
has to be pushed downstream.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:38:00 +03:00
Jan Alexander Steffens (heftig) 72941b5dc5 livesync: Improve EOS handling
I've looked at the GstQueue code again and tried making livesync behave
better with EOS. This isn't very well tested, though. My goal was to
make this look saner but I think this should be reviewed by someone who
knows the queue code.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1267>
2023-07-05 14:37:55 +03:00
Mathieu Duponchelle 0954af10c7 webrtc/signalling: fix race condition in message ordering
Spawning one task per message to send out instead of sending them out
sequentially from the one task used to poll the handler sometimes
resulted in peers receiving ICE candidates before SDP offers, triggering
hard to understand errors in the browser.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1254>
2023-06-20 22:30:01 +02:00
Sebastian Dröge dfe2442c92 webrtc/signalling: Allow unknown clippy lints
tracing is adding some that require a newer Rust version than used here.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 20:37:53 +03:00
Sebastian Dröge 84d6298729 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:08:02 +03:00
Mathieu Duponchelle 82f3910453 webrtcsink: don't try to use cudaconvert if not present
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:03:04 +03:00
François Laignel 460d6e1a74 mp4, fmp4: fix byte order for opus extension
The "Encapsulation of Opus in ISO Base Media File Format" [1] specifications,
§ 4.3.2 Opus Specific Box, indicates that data must be stored as big-endian.

In `write_dops`, `to_le_bytes` variants were used.

Related to [2].

[1] https://opus-codec.org/docs/opus_in_isobmff.html#4.3.2
[2] https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4875

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:55 +03:00
Mathieu Duponchelle 55a6609fdb webrtcsrc: add twcc extension to codec-preferences when present
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:48 +03:00
Seungha Yang 9f6b0d426c mccparse: Map timecode to PTS directly without offset
Assumes that caption stream's timeline starts from zero,
and maps timecode time_since_daily_jam() to PTS directly without
subtracting the first seen timecode.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:40 +03:00
Guillaume Desmottes 6f75243c8f fallbackswitch: add 'stop-on-eos' property
Fix the following use case:
- main input of fallbackswitch is finite (a media file)
- fallback input is infinite (videotestsrc)
- main input is done and send eos, which is propagated downstream
- fallbackswitch switches to fallback, sending STREAM_START which reset
  EOS downstream (aggregator does that)
- fallback input keeps pushing buffers forever.

Solve it by adding a 'stop-on-eos' property so fallbackswitch stops
pushing property once the main input is eos.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:33 +03:00
Guillaume Desmottes 68faccdacf fallbackswitch: remove unused SinkState::eos
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:28 +03:00
Guillaume Desmottes aa1d89aa9f fallbackswitch: log when handling events
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:23 +03:00
Sebastian Dröge 05b2caec74 webrtc: Update to fastrand 2
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:02:16 +03:00
Sebastian Dröge c9b2c88469 Use MPL as license specifier for plugins only requiring GStreamer < 1.20
And use MPL-2.0 for all that require GStreamer 1.20 or newer. The new
string is only allowed in 1.20 or newer and using it in older versions
causes failure to load the plugin.

All affected plugins are of course still MPL-2.0 licensed.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/374

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1249>
2023-06-19 19:01:52 +03:00
Sebastian Dröge 530aab7af6 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 23:46:16 +03:00
Mathieu Duponchelle e64e12e478 net/aws/transcriber: track discont offset in input stream
and add it up to subsequent transcripts.

This ensures synchronization is maintained even after the input stream
experiences a discontinuity and a gap in its timestamps.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 23:45:05 +03:00
Guillaume Desmottes 3a98bacd39 uriplaylistbin: use thiserror
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:54:34 +03:00
Guillaume Desmottes e33543dbfa uriplaylistbin: example: display iterations
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:53:51 +03:00
Guillaume Desmottes 0285cb1339 uriplaylistbin: example display when leaving because of eos
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:53:45 +03:00
Guillaume Desmottes 9b97e68da3 uriplaylistbin: prevent deadlock when notifying property changes
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:53:40 +03:00
Sebastian Dröge 9ae514f986 fmp4mux: Don't wait for more data if a stream has no GOP starting before fragment end
Simply don't output anything for this stream and only include it in the
future.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:53:28 +03:00
Sebastian Dröge e8bd521154 fmp4mux: Consider a stream filled if the earliest GOP starts after the current chunk
There's not going to be any buffer to output for this stream in the
current chunk.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:53:19 +03:00
Edward Hervey 60ae3fc0b9 rtpgccbwe: Improve packet handling
Both the delay-based *and* loss-based estimates should be computed instead of
just one. This ensures faster adaptation.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:53:07 +03:00
Sebastian Dröge 2002c54582 whipsink: Request pads with webrtcbin's pad templates and not our own
It's invalid to request pads with a pad template that is not part of the
element, and results in a critical warning.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:52:53 +03:00
Mathieu Duponchelle 8248425905 webrtcsink: further refactor connection to stats signals
- Stop passing webrtcbin around without using it

- Stop using glib::closure as clippy complains when using a unit type
  default-return

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:52:43 +03:00
Mathieu Duponchelle e9d32fb221 webrtcsink: fix stats_sigid logic
First off, we just created the session, we know stats_sigid is None
at this point.

Second, don't first assign the result of connecting on-new-ssrc to the
field, then the result of connection twcc-stats, that simply doesn't
make sense.

Finally, actually check that stats_sigid *is* None before connecting
twcc-stats, as I understand it this must have been the original
intention / behavior.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:52:34 +03:00
Mathieu Duponchelle 8ff2c6609c webrtcsink: don't panic in twcc-stats callback
If webrtcbin was disposed of at this point, simply return

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/345
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:52:22 +03:00
Arun Raghavan d2947ed1f3 Revert "fmp4: Return a running time in get_next_time()"
This reverts commit 04bb7b4db0.

As Sebastian points out, the chunk PTS is already in running time, so
this was wrong from the start.

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/363
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1234>
2023-06-06 22:52:02 +03:00
Sebastian Dröge df22c37268 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 19:09:42 +03:00
Thibault Saunier 0b65a2f8af webrtcsrc: Do not pass raw caps in the transceiver
That was not making sense.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 18:30:08 +03:00
Thibault Saunier 482ff879a4 webrtcsrc: Fix caps used when creating transceiver
We used to pass all media keys and attributes to the caps which
incorrect. Instead we should be using only the keys from the map
and remove all information related to rtcp which is irrelevant
to create the transceiver.

This also simplifies the code.

New caps look like:

```
Caps(
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 96,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "VP8",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 102,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "H264",
        packetization-mode: (gchararray) "1",
        profile: (gchararray) "baseline",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 104,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "H264",
        packetization-mode: (gchararray) "0",
        profile: (gchararray) "baseline",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 106,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "H264",
        packetization-mode: (gchararray) "1",
        profile: (gchararray) "constrained-baseline",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 108,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "H264",
        packetization-mode: (gchararray) "0",
        profile: (gchararray) "constrained-baseline",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 127,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "H264",
        packetization-mode: (gchararray) "1",
        profile: (gchararray) "main",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 39,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "H264",
        packetization-mode: (gchararray) "0",
        profile: (gchararray) "main",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 98,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "VP9",
        profile-id: (gchararray) "0",
    },
    application/x-rtp(memory:SystemMemory) {
        media: (gchararray) "video",
        payload: (gint) 100,
        clock-rate: (gint) 90000,
        encoding-name: (gchararray) "VP9",
        profile-id: (gchararray) "2",
    },
)
```

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 18:30:08 +03:00
Seungha Yang cc59ff9052 fallbacksrc: Don't apply fallback-audio-caps to the main audio stream
Intended behavior is configuring audio convert/resample elements
only for the fallback stream and also fallback-audio-caps is set.
Video and image stream are doing it as intended already.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 18:28:29 +03:00
Guillaume Desmottes 015edb7d37 fallbackswitch: document the pad priority ordering
I just wasted lots of time trying to figure out why my higher priority
pad wasn't used...

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 18:28:24 +03:00
Sanchayan Maity e632d24dda videofx: border: Do not advertise I420 for non-zero border radius
In certain cases, roundedcorners would negotiate to I420 even when user
supplied a non-zero border radius.

For example, the below pipeline leads to I420 being negotiated even
though a non-zero border radius was given. Ideally, this pipeline
should have failed at the negotiation stage.

```bash
gst-launch-1.0 -v \
   videotestsrc num-buffers=1000 pattern=white ! \
   video/x-raw,width=320,height=180 ! \
   roundedcorners border-radius-px=10 ! videobox border-alpha=0 top=-10 left=-10 right=-10 bottom=-10 fill=yellow ! \
   compositor name=comp sink_0::xpos=960   sink_0::ypos=0  sink_0::width=320 sink_0::height=180 sink_0::alpha=1.0 sink_1::xpos=960 sink_1::ypos=180  sink_1::width=320 sink_1::height=180 sink_1::alpha=1.0  \
   sink_2::xpos=960 sink_2::ypos=360  sink_2::width=320 sink_2::height=180 sink_2::alpha=1.0 sink_3::xpos=0 sink_3::ypos=0  sink_3::width=960 sink_3::height=720 sink_3::alpha=1.0 ! \
   video/x-raw,width=1280,height=720! x264enc ! mp4mux ! filesink location=test.mp4 \
   videotestsrc num-buffers=1000 pattern=red ! \
   video/x-raw,width=320,height=180 ! roundedcorners border-radius-px=10 ! comp. \
      videotestsrc num-buffers=1000 pattern=blue ! \
   video/x-raw,width=320,height=180 ! roundedcorners border-radius-px=10 ! comp. \
      videotestsrc num-buffers=1000 pattern=green ! \
   video/x-raw,width=960,height=720 ! roundedcorners border-radius-px=10 ! comp.
```

If border radius is non-zero, we should not really allow negotiation
to select I420. Fix this by returning only A420 for border-radius > 0
in `transform_caps` instead of returning both like earlier.

Another example of a simpler pipeline like below which would earlier work

```bash
gst-launch-1.0 videotestsrc pattern=red ! videoconvert ! video/x-raw,width=1923,height=1087,format=I420 ! roundedcorners border-radius-px=40 ! video/x-raw,format=I420 ! videoconvert ! gtksink
```

now fails with

```bash
WARNING: erroneous pipeline: could not link roundedcorners0 to videoconvert1, roundedcorners0 can't handle caps video/x-raw, format=(string)I420
```

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 18:28:17 +03:00
Antonio Kevo 5bbbd7bd23 fmp4: Use updated start_pts when checking stream filled
After calculating the earliest pts, the fragment_start_pts and
chunk_start_pts in State are updated. However, when checking if the
stream is filled, the previous start_pts (set to None) is used instead.
This means that chunk_filled and fragment_filled will be false the first
time aggregate() is called, assuming timeout is false, all_eos is false,
and the sinkpad is not EOS. This requires aggregate() having to be
called a second time before the first fragment is sent.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1216>
2023-05-18 18:28:08 +03:00
Sebastian Dröge 25fce80de6 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1204>
2023-05-09 18:31:20 +03:00
Sebastian Dröge 3c58f495ab deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1204>
2023-05-09 16:56:41 +03:00
Nick Steel adfceaea77 spotify: check cached creds username before use
If a username was specified, only use cached credentials that match
that username.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1204>
2023-05-09 16:55:38 +03:00
Jan Beich fc5ff9503d gtk4: unbreak wayland, x11egl, x11glx features on non-Linux
As the features are non-default leave the responsibility to filter by
platform to consumers.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1204>
2023-05-09 16:50:43 +03:00
Sebastian Dröge e31fd2dcc5 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1190>
2023-04-22 12:44:42 +03:00
Lily Foster 790383313d meson: support rust cross-compiling with cargo wrapper
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1190>
2023-04-22 12:44:42 +03:00
John King a434607515 spotify: fix credentials cache
Cache Spotify response instead of username and password.
This should resolve frequent "New login to Spotify" emails.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1190>
2023-04-22 12:44:42 +03:00
Lily Foster 57fe64b748 meson: avoid passing the --features flag to wrapper when empty
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1190>
2023-04-22 12:10:55 +03:00
Edward Hervey f4a565d4ea rtpgccbwe: Don't process empty lists
The structure parsing could result in an empty vector. Don't do any processing
since the loss code assumes it's non-empty for average estimates which would
result in weird/invalid results.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1190>
2023-04-22 12:10:43 +03:00
Arun Raghavan 5462ed0e95 ffv1dec: Drop rank for now
We'll keep the rank lower than avdec_ffv1, at least until we're
comfortable with support for the entire range of possible inputs working
well.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1190>
2023-04-22 12:10:28 +03:00
Sebastian Dröge d67eb1d405 meson: Update version 2023-04-06 16:47:04 +03:00
Sebastian Dröge 1b007178af Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 19:55:38 +03:00
Sebastian Dröge e0a7c93d46 ndisrc: Fix copying of raw video frames with different NDI/GStreamer strides
And also don't copy each line twice for single-plane formats.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:17:31 +03:00
Sebastian Dröge 00d1c73ff1 deny: Update for older versions of the windows bindings
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:17:26 +03:00
Tim-Philipp Müller 2d56989f5c git: replace LICENSE file symlinks with copies
Git will de-duplicate the contents for us anyway, and
symlinks can cause problems with some versions of git
and also on Windows.

https://github.com/mesonbuild/meson/issues/11646
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/4326

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:17:16 +03:00
Sebastian Dröge f7c81e78f5 fallbacksrc: Don't check caps when linking to the fallbackswitch
Downstream might have different caps requirements and linking might
fail. Instead of having linking fail, give upstream an opportunity to
reconfigure and otherwise have a normal negotiation error during data
flow.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/334

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:17:10 +03:00
Guillaume Desmottes 25e9fc55b7 uriplaylistbin: example: add queues
Prevent pipeline starvation with some media such as
https://assets.onestream.live/studio/Videos/1080p/osl-interval-1080p-8.mp4

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:17:05 +03:00
Vivia Nikolaidou f56f40a943 livesync: Actually assume zero upstream latency when query fails
The code said "assuming zero" but left latency at None instead of
Some(0), failing to unwrap the value later.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:16:16 +03:00
Mathieu Duponchelle c2d6273786 webrtcsink: fix calculation of fec_ratio with multiple encoders
In this context, the bitrate variable is for all encoders, but the
max_bitrate field is per encoder. To calculate a proper FEC ratio, we
need to scale max_bitrate to the number of encoders.

+ Also clamp the fec-percentage that we set on the transceiver for extra
  safety

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:16:10 +03:00
Sebastian Dröge d38e2751cc deny: Update for duplicated old dependencies in dependencies
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:16:04 +03:00
Sebastian Dröge e80068deca deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:15:32 +03:00
Mathieu Duponchelle 1219c27fa4 transcriberbin: fix deadlock on construction error
Don't post an error message on the bus while holding the state lock

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:15:26 +03:00
Mathieu Duponchelle bd6b2e0fab tttocea608: fix disappearing text after special character in non-popon
To avoid special characters getting de-duplicated by the decoder, we
insert no-op control commands after those. The no-op command must be
picked according to the mode we're in however, inserting
"resume_caption_loading" commands in roll-up mode caused obvious issues.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:15:20 +03:00
Mathieu Duponchelle fe15b06ab6 tttocea608: fix pushing unfixed caps downstream
Allowed downstream caps might hold multiple structures, simply fixating
the first structure is not enough, tttocea608 must also create caps with
a single structure from there (or remove the remaining structures, but
new caps seems cleaner)

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:15:14 +03:00
Lily Foster 5c3b5842b0 update-version.sh: Also update versions in Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:15:07 +03:00
Mathieu Duponchelle 8274be3c0b transcriberbin: fix initial transcription bin setup
When passthrough=false at construction and the transcription bin
is linked after receiving video caps (and not on state change),
there could be a race where transcription-bin was linked with
tee but state change of the transcription-bin was not finished.

If upstream pushed a buffer at that point, it got a flushing flow
return and stopped streaming.

This is the same issue and the same fix as 558656deb5
for the initial passthrough=false case.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:14:59 +03:00
Sebastian Dröge 882e456b3e deny: Update for new syn/bitflags versions
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1161>
2023-04-05 18:14:52 +03:00
Sebastian Dröge cb92c31aa9 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1140>
2023-03-19 17:44:05 +02:00
Carlo Cabrera c5dbacb6a7 gtk4: Fix compilation on macOS
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/332

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1140>
2023-03-19 17:40:42 +02:00
Sebastian Dröge b3b8d73ce1 threadshare: jitterbuffer: Rename C symbols to avoid conflicts with the same symbols from the rtpmanager plugin
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/326

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1140>
2023-03-19 17:40:40 +02:00
Sebastian Dröge 2da4701b2a Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1135>
2023-03-14 13:46:58 +02:00
Josef Kolář 748b0530c4 fmp4mux: fix hls_live example
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1135>
2023-03-14 13:44:30 +02:00
Thibault Saunier 35a1d8f504 meson: Handle features detection for gst version in a script
Instead of having a big list of features in the meson.build file, we
reuse the information from the Cargo.toml files

This refactors the dependencies to handle that new use case

There were issue in previous handling and only activating the `webrtc`
plugin was failing because the list of features incorrect.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/295

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1135>
2023-03-14 13:44:24 +02:00
Arun Raghavan 6d248c70dd fmp4: Return a running time in get_next_time()
We were currently returning a value based on the next chunk PTS, but the
expectation in GstAggregator is that we return a running time. This
resulted in spurious wakeups and warnings like:

0:00:01.501685123 1552995 0x55899715c1e0 WARN                 fmp4mux mux/fmp4/src/fmp4mux/imp.rs:1818:gstfmp4::fmp4mux:👿:FMP4Mux::drain_buffers:<fmp4mux0:sink_1> Don't have a complete GOP for the first stream on timeout in a live pipeline

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1135>
2023-03-14 13:44:13 +02:00
Talha Khan 62cac113fb livesync: Support variable framerate in fallback buffer duration calc
Avoids a divide by zero error

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1135>
2023-03-14 13:43:41 +02:00
Guillaume Desmottes c9719e4231 uriplaylistbin: reset element when switching back to NULL
Allow us to re-use the element later.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1135>
2023-03-14 13:43:22 +02:00
Thibault Saunier 01f0c0a330 cargo.lock: Downgrade clap to support rustc 1.64
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1117>
2023-03-02 21:08:11 +02:00
Thibault Saunier 4b867d27fe Add a webrtcsrc element
Updating the docker image to include:
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3236

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1117>
2023-03-02 14:56:30 -03:00
Sebastian Dröge f2b03d3796 Update Cargo.lock 2023-03-02 13:26:19 +02:00
Sebastian Dröge 9a779607c7 Update versions to 0.9.10 2023-03-02 13:18:00 +02:00
Sebastian Dröge ccfa25aa60 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:04:47 +02:00
Sebastian Dröge a0cfe054c8 deny: Update to allow socket2 0.4
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:01:18 +02:00
Sebastian Dröge e2c7e7ebe1 deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:01:18 +02:00
Sebastian Dröge f5feb34fcb deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:01:18 +02:00
Vivia Nikolaidou a0fe1aba5f ndisinkcombiner: Properly handle caps changes
We are caching one video buffer, so previously we were changing the src
caps one buffer too early.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:01:18 +02:00
Sebastian Dröge fef81b1c97 threadshare: Update to socket2 0.5
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:01:18 +02:00
Thibault Saunier e4c9ba43df webrtc: Enhance debug messages when using unknown peer ID
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 11:01:18 +02:00
Guillaume Desmottes e14777573f tracers: queue_levels: add appsrc support
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 10:53:19 +02:00
Sebastian Dröge 4aacf4d3ad livesync: Correctly calculate fallback buffer duration from framerate
Numerator and denominator were switched.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 10:53:19 +02:00
Matthew Waters 0d3dc25414 webrtcsink: also support nvvidconv in lieu of nvvideoconvert
nvvideoconvert may not exist and nvvidconv might on some Jetson
platforms.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1116>
2023-03-02 10:53:19 +02:00
Sebastian Dröge ef7f0d12bf gtk4: Set sync point on the video frame after mapping it
Otherwise it is not always ready for use yet in GTK even after waiting
on the sync point, and a fully transparent texture is rendered instead.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/320

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1106>
2023-02-25 12:52:46 +02:00
Sebastian Dröge 2723fc4713 gtk4: Attach channel receiver to the default main context from the main thread
It requires acquiring the main context for thread-safety reasons and
that is only possible from the main thread itself.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/319

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1101>
2023-02-22 14:24:51 +02:00
Sebastian Dröge e100506194 gtk4: Don't unnecessarily set the sink to READY to retrieve the context
That's not needed and will cause the GL context messages to be not
distributed inside the pipeline.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1101>
2023-02-22 14:24:49 +02:00
Sebastian Dröge 5c21b10841 gtk4: Refactor and simplify GL context handling
Create a single, global GDK GL context and the corresponding GStreamer
GL display and wrapped GStreamer GL context when initializing the first
sink and continue using that for all further sinks.

Additionally, don't create a full GStreamer GL context inside the sink
but only distribute the wrapped GL context in the pipeline so that
elements that actually need a full GL context can create one that is
sharing with that one. The sink itself does not need a full GStreamer GL
context.

Then inside the sink check that any GL memory that arrives was created
by a GL context that can share with the wrapped GDK GL context and only
then use it.

And lastly, use the correct GL contexts for a) creating a sync point and
b) actually waiting on it.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/318

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1101>
2023-02-22 14:24:21 +02:00
Seungha Yang 8f612b9003 mp4mux: Ignore framerate update
like mp4mux in -good does already

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1098>
2023-02-21 16:17:59 +02:00
Seungha Yang 365dcfa730 fmp4mux: Ignore framerate update
like mp4mux in -good does already

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1098>
2023-02-21 16:17:52 +02:00
Arun Raghavan 611c7d6cd3 hlssink3: Allow GIOStream signal handlers to return None
If creating a playlist or fragment stream fails (disk is full, the
directory is removed, ...), we will currently crash because the signal
handler expects a non-None GIOStream. The actual callback is allowed to
return None values and we handle this in the caller, so let's not have
this restriction on the signal handler.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1098>
2023-02-21 16:17:45 +02:00
Sebastian Dröge bfbde450db Add mp4 plugin to README.md
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1098>
2023-02-21 16:17:38 +02:00
Seungha Yang 562b429388 rtpav1pay: Fix Leb128Bytes size parsing
There are multiple ways of encoding the value, and don't assume
that bitstream used the way used in this plugin. Instead, count
the number of used bytes.

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/312
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1092>
2023-02-11 19:44:51 +02:00
Sebastian Dröge 09cffe0e70 Update Cargo.lock 2023-02-09 22:08:26 +02:00
Sebastian Dröge eb3d3b3088 Update versions to 0.9.9 2023-02-09 22:08:17 +02:00
Sebastian Dröge ee46b103b8 ci: Update 0.19 gstreamer-rs CI template
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Sebastian Dröge 8a384aa8b2 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
rajneeshksoni 01d3b0f9da awss3sink: Add properties to set content-Type and content-disposition.
for uploaded object default content-type is set to binary/octet-stream,
which is correct.
metadata cannot be used to set content-type and content-disposition as
setting metadata add a prefix x-amz-meta to key
e.g. setting metadate "content-type=video/mp4" actually set value as
x-amz-meta-content-type. So these has to be seaprate property.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Sebastian Dröge 58a6dbacca fmp4mux: Pass one more buffer in test_buffer_multi_stream_short_gops test
This works around non-determinism in aggregator where depending on
timing it can happen that it consumes all buffers from both pads or
waits for another buffer on one pad while the other one already has one.

The effect in this test was that it sometimes timed out. By providing
one more buffer it is guaranteed now that at this point the muxer is
beyond the end of the first fragment.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Sebastian Dröge e52bb160c7 fmp4mux: Accept more data on already filled streams if the remaining streams need more data for finishing a GOP
In other words, continue queueing buffers in sync from all streams until
all of them are ready for draining instead of stopping to queue buffers
on every stream that is already filled individually.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/310

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) 19c527a9c5 livesync: Document State's fields
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) fbf50b395d livesync: Improve formatting
Move some code around to make it a bit more readable. No change in
behavior.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) 9d67753fd6 livesync: Fix log message capitalization
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) 07adcf5ecf livesync: Extract LiveSync::flow_error
And add details so it behaves more like the `GST_ELEMENT_FLOW_ERROR`
macro.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) 35552dc73c livesync: Extract audio_info_from_caps
And adjust it slightly so it never panics.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) 1e67259462 livesync: Move single segment prop
Keep it with the settings, not after the stats.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Jan Alexander Steffens (heftig) cccf90f59e livesync: Fix queueing
The logic of the element requires the next buffer to be available
immediately after we are done pushing the previous, otherwise we insert
a repeat.

Making the src loop handle events and queries broke this, as upstream is
almost guaranteed not to deliver a buffer in time if we allow non-buffer
items to block upstream's push.

To fix this, replace our single-item `Option` with a `VecDeque` that we
allow to hold an unlimited number of events or queries, but only one
buffer at a time.

In addition, the code was confused about the current caps and segment.

This wasn't an issue before making the src loop handle events and
queries, as only the sinkpad cared about the current segment, using it
to buffers received, and only the srcpad cared about the current caps,
sending it just before sending the next received buffer.

Now the sinkpad cares about caps (through `update_fallback_duration`)
and the srcpad cares about the segment (when not in single-segment
mode).

Fix this by
  - making `in_caps` always hold the current caps of the sinkpad,
  - adding `pending_caps`, which is used by the srcpad to store
    caps to be sent with the next received buffer,
  - adding `in_segment`, holding the current segment of the sinkpad,
  - adding `pending_segment`, which is used by the srcpad to store
    the segment to be sent with the next received buffer,
  - adding `out_segment`, holding the current segment of the srcpad.

Maybe a fix for
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/298.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Simon Himmelbauer fffecca624 spotifyaudiosrc: Support configurable bitrate
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
rajneeshksoni f96b64e1c1 hlssink3: Allow setting i-frame-only playlist.
HLS allows manifest where all segments are single ifames.
This manifest requires `EXT-X-I-FRAMES-ONLY` tag in the
manifest.
I-FRAMES-ONLY playlist segments are video only segments.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Sebastian Dröge c805c3bb3a dav1ddec: Make sure to call get_picture() twice in a row when draining
The first time might return `EAGAIN` if there are pending frames but
there is no decoded frame available yet. The second time it will
actually wait for frames to become available and only start returning
`EAGAIN` again once no more frames are left.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Sebastian Dröge 5f70c0f5fe rtpgccbwe: Don't use clamp() if there's no clear min/max value
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/305

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:43:57 +02:00
Sebastian Dröge da7743d2e7 fmp4mux: Handle GOPs ending after the desired fragment end correctly
Either create further chunks if enough data is queued or simply start
the new fragment at a later time if the keyframe is later.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:17:51 +02:00
Sebastian Dröge dae1d8b5ef mp4/fmp4: Update docs
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:17:34 +02:00
Sebastian Dröge 7ba1100a92 mp4: Add support for AV1
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:17:23 +02:00
Sebastian Dröge a65feb7ef9 fmp4: Add support for AV1
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:16:40 +02:00
Sebastian Dröge 1029669427 fmp4: Add support for VP8
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:15:50 +02:00
Sebastian Dröge 40cada5f69 mp4: Add support for VP8
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:15:05 +02:00
Sanchayan Maity 0e55e19d57 aws/s3hlssink: Fix deadlock on EOS
In state change to NULL, we take state lock and call stop. When stop
is called, we will try to upload queued segments in S3 request thread.
That tries to take the state lock again and deadlocks.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:14:06 +02:00
Sanchayan Maity 7a8ecb5343 aws/s3hlssink: Use factory name when checking name of child element
Commit ad3f1cf fixed the name of hlssink child element to be the same
for hlssink2 and hlssink3. However, we rely on element name to return
boolean in case of hlssink3 or None in case of hlssink2 as the return
value of the delete-fragment closure.

Fix this by using the factory name instead of the element name.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:14:01 +02:00
Sebastian Dröge 17dec1cb26 rtpav1pay: Add support for tu/frame aligned input
In this case every buffer can be sent out immediately and makes up a
whole frame.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:13:40 +02:00
Sebastian Dröge ba0904630d rtpav1pay: Consider the marker flag to output packets immediately at the end of a frame
Otherwise it is necessary to wait for the beginning of the following
frame, which unnecessarily increases the latency.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/255

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:13:33 +02:00
Sebastian Dröge af0e6281d2 rtpav1depay: Fix depayloading of packets starting with a leading OBU fragment followed by more OBUs
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/288

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:13:26 +02:00
Sebastian Dröge e79221f386 rtpav1depay: Fix error handling
Don't error out immediately on errors anymore but try again with the
next packet.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/289

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:13:19 +02:00
Sebastian Dröge dc47b35536 rtpav1depay: Set DISCONT flag on buffers following a corrupted packet
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:13:13 +02:00
Sebastian Dröge 3520fc67de rtpav1depay: Don't output full TUs but just OBUs as they come
Simplifies state tracking and potentially reduces latency as it's not
necessary to wait until all fragments of an OBU are received.

The last OBU of a TU is marked with the marker flag to allow parsers to
detect this without first seeing the beginning of the next TU.

Also use a simple `Vec` for collecting complete OBUs instead of a
`gst_base::Adapter` as this reduces the number of allocations.

And also handle invalid packets a little bit more gracefully.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/244

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:13:06 +02:00
Jan Alexander Steffens (heftig) 402d96b80c livesync: Only resend segment if not in single-segment mode
In single-segment mode, the outgoing segment does not change when the
incoming segment changes. We only need to resend the segment if we got
flushed or deactivated.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:07:06 +02:00
Sebastian Dröge 68bec4a0db fmp4mux: Fix a couple of assertions by handling these cases cleaner
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:06:56 +02:00
Sebastian Dröge adbb8b6495 fmp4mux: Refactor and clean up code
Split many longer functions into multiple functions and simplify various
parts. Overall this is functionally still the same as before.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:06:56 +02:00
Sebastian Dröge a01437b675 fmp4mux: Add support for sub-fragments / chunking
Allow outputting sub-fragments (chunks in CMAF terms) that are shorter
than the fragment duration and don't usually start on a keyframe. By
this the buffering requirements of the element is reduced to one chunk
duration, as is the latency.

This is used for formats like low-latency / LL-HLS and DASH.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:06:56 +02:00
Guillaume Desmottes b9e203d6c1 fmp4mux: add 'offset-to-zero' property
Add it only to 'isofmp4mux', the onvif variant already does this and
CMAF and DASH are always single-stream so you rely on inter-container
synchronization via the running-time.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:06:08 +02:00
Arun Raghavan f3b8288ef9 aws: s3hlssink: Fix the name of the hlssink child element
It's easier to set child element properties if the name doesn't depend
on the factory.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:05:58 +02:00
Sebastian Dröge 4eeb8ffb63 fmp4mux: Don't write the first sample flags into any trun but the first
The flags are based on the first sample of this fragment so writing it
into any trun but the first is not very useful.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:05:48 +02:00
Sebastian Dröge 01aa9380d4 fmp4mux: Fix decision whether per-sample flags are needed in the trun
Previously it would never use per-sample flags if any later sample
needed different flags than the first two.

Also comment the code a bit better.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1086>
2023-02-09 21:05:40 +02:00
Sebastian Dröge d9e9468f9a meson: Update version to 0.9.8 2023-01-24 15:46:09 +02:00
Sebastian Dröge 59f575888e Downgrade clap dependency to 4.0
4.1 does not build with Rust 1.63 anymore.
2023-01-23 11:33:20 +02:00
Sebastian Dröge 74a40060ce Update Cargo.lock 2023-01-23 11:30:40 +02:00
Sebastian Dröge 5c2582d105 Update version to 0.9.8 2023-01-23 11:30:27 +02:00
Sebastian Dröge 4b9392938f dav1d: Don't treat any kind of bitstream error immediately as fatal
Instead use the videodecoder error handling to allow up to max-errors
consecutive decoding errors, i.e. infinite by default in 1.22 and newer.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1058>
2023-01-23 11:08:49 +02:00
Sebastian Dröge 407a367529 dav1d: Get rid of some unnecessary unwrap()s
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1056>
2023-01-22 00:59:54 +02:00
Sebastian Dröge 88a437ac32 dav1d: Remove unnecessary frame dropping loop
After flushing there are no frames left anymore that could be dropped.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1056>
2023-01-22 00:59:52 +02:00
Sebastian Dröge 853acfc4fe dav1d: Don't flush the decoder when draining
This directly discards all frames and it won't be possible to output
them anymore.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1056>
2023-01-22 00:59:51 +02:00
Sebastian Dröge 3cd6074a8e dav1d: Only drain at most one decoded frame per input frame unless the decoder requires more before accepting new data
This works around a race condition in dav1d where the decoder deadlocks
if multiple threads are used, and also is generally beneficial as it
allows for proper frame threading.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1056>
2023-01-22 00:59:48 +02:00
Sebastian Dröge ab5ee0511b Update Cargo.lock 2023-01-19 19:06:52 +02:00
Sebastian Dröge 4ba452dcc3 Update versions to 0.9.7 2023-01-19 19:06:43 +02:00
Sebastian Dröge 711313c4c5 gtk4: Only provide a buffer pool to upstream if it requested one 2023-01-19 16:40:45 +02:00
Sebastian Dröge c83f48f0a1 gtk4: Make no caps in the allocation query a non-error 2023-01-19 16:40:26 +02:00
Sebastian Dröge 101bcbc1a0 gtk4: Asynchronously flush frames from GDK
There is no need to wait until the frames are flushed as the textures
will be kept alive until GDK is finished with them, and doing so can
cause deadlocks.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/287

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1053>
2023-01-19 15:40:04 +02:00
Sebastian Dröge 2a68be2000 gtk4: Keep GstGLMemory alive as long as it is used inside GDK
Otherwise the texture might be released in the meantime and GDK would
use an invalid GL texture ID.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/287

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1053>
2023-01-19 15:40:03 +02:00
Sebastian Dröge 3ea77d7a74 Update Cargo.lock 2023-01-18 17:19:28 +02:00
Sebastian Dröge c818a575b4 Update versions to 0.9.6 2023-01-18 17:19:17 +02:00
Sebastian Dröge 43e5bd7b3a Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1050>
2023-01-18 16:57:28 +02:00
Guillaume Desmottes c6158b7a4e livesync: fix late-threshold property min value
The code is handling 0 as "always over threshold" but it was not
possible to set the property to 0.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1050>
2023-01-18 16:56:34 +02:00
Philippe Normand 27f5b5cc33 meson: Only enable cargo features when options are enabled (bis)
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/285 even more.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1050>
2023-01-18 16:56:24 +02:00
Sebastian Dröge d02508a7d0 aws: Update to AWS SDK 0.53/0.23
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1050>
2023-01-18 16:56:10 +02:00
Sebastian Dröge df3b90881f Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1045>
2023-01-11 18:38:47 +02:00
Mathieu Duponchelle 53ae335d22 webrtcsink: fix panic on pre-bwe request error
We dispose of consumer pipelines asynchronously, potentially after the
session objects have been disposed of.

As session objects are the owner of the cc element, it is entirely
possible for the bwe-request signal to get emitted after cc has been
disposed of, as the closure only takes a weak reference to it.

Fix by simply checking if cc is None

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1045>
2023-01-11 18:38:13 +02:00
Sebastian Dröge c8e8af3e81 deny: Ignore duplicated base64 dependency for now
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1045>
2023-01-11 18:38:13 +02:00
Sebastian Dröge 8e0fc8b063 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1042>
2023-01-10 20:20:56 +02:00
Nirbheek Chauhan cc8da54adb meson: Only enable cargo features when options are enabled
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/285

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1042>
2023-01-10 20:20:24 +02:00
Sebastian Dröge e213ba9618 deny: Remove duplicated windows dependencies
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1042>
2023-01-10 20:20:24 +02:00
Sebastian Dröge e8df0a0cb7 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1040>
2023-01-10 10:31:23 +02:00
Sebastian Dröge 408d439631 rav1e: Enable threading support
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1040>
2023-01-10 10:30:34 +02:00
Sebastian Dröge 2f623e15c2 Update Cargo.lock 2023-01-07 16:06:29 +02:00
Sebastian Dröge 2a8a90f76f Update versions to 0.9.5 2023-01-07 16:06:17 +02:00
Sebastian Dröge cd3e333a0c Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:59:53 +02:00
Sebastian Dröge 1bfe6f9142 gtk4: Update dependencies to releases
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:59:26 +02:00
Sebastian Dröge db9ef0b2af gtk4: Propagate the GL display to the remainder of the pipeline
This allows sharing it with other parts of the pipeline and avoids
creating different, incompatible displays/contexts in different parts of
the pipeline.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:49 +02:00
Sebastian Dröge 85a03f5ff0 fmp4mux: Remove obsolete comment
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:44 +02:00
Sebastian Dröge 4b936950c2 aws: Update to test-with 0.9
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:37 +02:00
rajneeshksoni 698ab100b3 awss3hlssink: Add stats property.
application can monitor the progress of hls segment generation
and upload progress.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:31 +02:00
Philippe Normand 517dc286d0 rtpav1depay: Implement srcpad set_caps
Without this auto-pluggers such as decodebin or parsebin will be unable to
process AV1 RTP payloads.

Tested with: `videotestsrc num-buffers=50 ! videoconvert ! av1enc ! av1parse ! rtpav1pay ! queue ! decodebin3 ! videoconvert ! queue ! autovideosink`

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:15 +02:00
Guillaume Desmottes 514a8e48ef textahead: fix previous buffers
Actually implement a proper queue.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:07 +02:00
Sebastian Dröge ff1c99df98 gtk4: Rename a variable to make more sense
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:25:00 +02:00
Sebastian Dröge 696944c08e gtk4: Handle more GL context creation failures gracefully
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:53 +02:00
Sebastian Dröge 37dedfd4d0 gtk4: Reset app context and display if GL context creation fails
No need to keep them around and that way we either have all 3 values set
or none of them.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:48 +02:00
Sebastian Dröge bb2f632c9c gtk4: Reduce number of unwraps during GL context creation and query handling
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:42 +02:00
Jordan Petridis f6b092d2af video/gtk4: Fix typo in info logs
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:30 +02:00
Nirbheek Chauhan cd5a93dc09 meson: Enable gstreamer-gl-1.0 features in gtk4 plugin
Basically, if gstreamer-gl-1.0 is built with wayland / x11 / egl, use
those features in the gtk4 plugin.

MacOS always uses CGL, and it's always available. Windows version does
not use GL yet.

Requires https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3654

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:24 +02:00
Nirbheek Chauhan a5a3c44951 cargo_wrapper: Write to log with line-buffering
So we get log output while cargo is running, not just when it completes

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:18 +02:00
Nirbheek Chauhan a0dbb94e01 gtk4: Remove 'gst' prefix from another debug category
Missed it last time. Caught all of them this time. Continuation from:

https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1029

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:12 +02:00
Nirbheek Chauhan 7013416a39 meson: Require gstreamer-gl-1.0 for gtkpaintablesink
This is required on macOS, and is also highly recommended on Linux.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:24:05 +02:00
Nirbheek Chauhan 9f8fa99089 gtk4: Use GL implicitly without the gst_gl feature on macOS
We already require gstreamer-gl as a dependency on macOS, so reflect
that in the code too.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:59 +02:00
Sebastian Dröge 34434bd877 gtk4: Add support for GL on macOS
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:53 +02:00
Nirbheek Chauhan d67baa7668 meson: Add an option to build examples
Required renaming threadshare/benchmark to threadshare/ts-benchmark
because 'benchmark' as a target name is reserved for meson's
`benchmark` target.

Disabled by default because cargo decides that it has to rebuild
everything, and is really slow because of that.

Also required adding --features for setting features required by the
examples.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:47 +02:00
Nirbheek Chauhan 8cdb30bd39 meson: Add options for all plugins
Required a slight rework of the build file, and how options are passed
to cargo_wrapper.py

Necessitated a bump of the required gstreamer version to 1.20, which
should be fine for the meson build since its primary function is to be
built as part of the gstreamer monorepo build to get a dev env.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:41 +02:00
Nirbheek Chauhan efc07cecf7 cargo_wrapper: Fix setting of PKG_CONFIG_PATH and CARGO_TARGET_DIR
Don't need to use an env var for the latter.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:36 +02:00
Nirbheek Chauhan 7db53aba22 meson: Require tomllib / tomli python modules explicitly
These are required by dependencies.py

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:29 +02:00
Nirbheek Chauhan 2045847bd6 gtk4: Remove 'gst' from gtksink debug category name
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1038>
2023-01-07 13:23:23 +02:00
Sebastian Dröge f4cb4b9da6 Update Cargo.lock 2022-12-27 13:15:11 +02:00
Sebastian Dröge b0bd55c4d2 Update versions to 0.9.4 2022-12-27 13:14:59 +02:00
Sebastian Dröge b9e6c817b7 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:53:56 +02:00
Sebastian Dröge e95a2c1016 gtk4: Release GStreamer GL context and display when going back to NULL state
And acquire it again next time when going to READY state.

Also clean up the whole GL context initialization.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge 31760b8f9a gtk4: Use glib::ThreadGuard instead of the fragile crate
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/272

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge 8d7ce380c4 gtk4: Don't try to use GL mapped video frames as raw RGB memory
This will fail badly because the memory pointers are actually GL texture
IDs, however this case can't really happen in practice so simply assert
on this.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge e8701652e2 gtk4: Don't error out when the main context channel does not exist anymore when rendering
But instead return flushing to shut down silently.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge 993619d654 gtk4: Flush frames from the paintable when shutting down the sink
Otherwise it will continue showing the last frames forever and keep
around the frames forever instead of rendering black.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/281

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Johan Bjäreholt d9d5571641 fmp4mux: Only push fragment_offset if write_mfra is true
This is done so that the fragment_offset vector does not infinitely
build up when write_mfra is disabled.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Nirbheek Chauhan ba889c143c meson: Disable webp plugin on Windows and macOS
Known to be broken, should be kept disabled till the fix is in
a release: https://github.com/qnighy/libwebp-sys2-rs/pull/13

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Nirbheek Chauhan 4b95bde38f meson: Handle windows path separator correctly
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Nirbheek Chauhan 041f51c4bb cargo_wrapper: Handle windows paths for depfiles
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge 161c6db641 deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge deeff67f94 aws: Update to AWS SDK 0.52/0.22
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Nirbheek Chauhan 568c2be582 meson: Fix pkgconfig detection when specified in machine file
When pkgconfig and pkg_config_path are specified in the machine file,
we need to parse those and pass them on to the cargo_wrapper.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Nirbheek Chauhan 548fe54ba9 meson: Do not serialize env, use env: kwarg
This is simpler, and more correct.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1027>
2022-12-27 12:39:56 +02:00
Sebastian Dröge a1afef2207 Update Cargo.lock
This time correctly.
2022-12-27 10:43:00 +02:00
Sebastian Dröge 778c4da27e Update Cargo.lock 2022-12-27 10:32:03 +02:00
Sebastian Dröge cbc99fb198 Revert "Revert "rav1e: Update to rav1e 0.6""
This reverts commit 5f6afce842.

It should be building fine on macOS too now.
2022-12-27 10:31:33 +02:00
Sebastian Dröge b701003352 livesync: Add missing version to the gst-plugin-gtk4 / gst-plugin-version-helper dependencies 2022-12-16 20:37:58 +02:00
Sebastian Dröge 5f9645bb74 Update Cargo.lock 2022-12-16 20:24:01 +02:00
Sebastian Dröge bae5294e8f Update versions to 0.9.3 2022-12-16 20:22:17 +02:00
Sebastian Dröge 19957d1d23 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:59:32 +02:00
Sebastian Dröge bc9408840f livesync: Use release versions of the GLib/GStreamer bindings
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:59:32 +02:00
Sebastian Dröge 08668a4bbb livesync: Fix version
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:59:32 +02:00
Sebastian Dröge fb745f077b fmp4mux: Skip gap buffers earlier to consider them for the sample durations and fragment start durations
Otherwise dropping the gap buffers would offset the timestamps of
following samples.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:59:32 +02:00
Sebastian Dröge 1eea2219c6 mp4mux: Adjust durations and possibly stream start time on encountering a gap buffer
If there was a previous sample in this stream then its duration needs to
be extended by the gap position, and if there was none then the start
time of the whole stream has to be shifted by the duration.

Not doing so causes timestamps to be offset wrongly by the duration of
the gap.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:52:03 +02:00
Sebastian Dröge 9b2d9ba4f9 mp4mux: Fix edit list shift for streams with initial DTS smaller earliest PTS but initial DTS positive
This would be a stream where the initial DTS is negative if the initial
PTS was zero, but it is offset so the initial DTS became positive now.
The edit list shift has to happen exactly the same way though.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:57 +02:00
Sebastian Dröge 00615ab478 mp4mux: Don't write gap edit lists if their duration would be zero
The track might start later than the earliest track by less than one
timescale units, in which case writing an empty gap edit list would be
useless and confusing.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:50 +02:00
Sebastian Dröge 6ceccac1be mp4mux: Don't write empty chunks at the end if the last buffer of a stream started a new chunk and happened to be a from a gap event
Empty chunks are not valid in MP4.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:30 +02:00
Jan Alexander Steffens (heftig) 6596b6cdd1 Add livesync plugin
It attempts to produce a (nearly) gapless live stream by synchronizing
its output to the running time and forwarding the next input buffer if
its start is (nearly) flush with the end of the last output buffer.

If the input buffer is missing or too far in the future, it duplicates
the last output buffer with adjusted timestamps. If it is operating on a
raw audio stream, it will fill duplicate buffers with silence.

If an input buffer arrives too late, it is thrown away. If the last
input buffer was accepted too long ago (according to `late-threshold`),
a late input buffer is accepted anyway, but immediately considered a
duplicate. Due to the silence-filling, this has no effect on audio, but
video gets a "slideshow" effect instead of freezing completely.

The "many-repeats" property will be notified when this element has
recently duplicated a lot of buffers or recovered from such a state.

Co-authored-by: Vivia Nikolaidou <vivia@ahiru.eu>
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:22 +02:00
Michiel Konstapel b5641d838e audiornnoise: Add debug output for voice activity to help you choose a threshold
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:15 +02:00
Mathieu Duponchelle fffd7dc542 webrtc/README: update command to run the signalling server
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/277

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:08 +02:00
Sebastian Dröge b4185134d1 Fix various new clippy warnings
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1017>
2022-12-16 18:51:00 +02:00
Sebastian Dröge dc7e3c9f28 Update Cargo.lock again 2022-12-12 20:04:13 +02:00
Sebastian Dröge 5f6afce842 Revert "rav1e: Update to rav1e 0.6"
Revert for the time being because it pulls in libgit2-sys, which fails
to build on macOS because of course it does. It regularly fails building
everywhere because of its brittle C code and build system, which is why
gst-plugin-version-helper moved away from it.

This reverts commit e6789fc338.
2022-12-12 19:01:40 +02:00
Sebastian Dröge 7b1ee9f948 webrtchttp: Remove unnecessary clippy warning override
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 14:31:33 +02:00
Sebastian Dröge b6c9c14ccf Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 14:21:30 +02:00
Sebastian Dröge 3936211b55 threadshare: Update to concurrent-queue 2
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:47:45 +02:00
Sebastian Dröge 2a981132b4 gtk4: Only require GTK 4.6 if GL support is enabled
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:46:57 +02:00
Michiel Konstapel a1fd847f70 audiornnoise: add voice detection threshold
Add a property "voice-activity-threshold". Frames where the voice
detection score from the RNN is below the threshold will be completely
muted.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:40:01 +02:00
Sebastian Dröge 71558bd086 gtk4: Deactivate application GL context again after fill_info()
It does not need to be activate anymore, and keeping it active can cause
problems.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:55 +02:00
Jordan Petridis b689a0825e gtk4: Deactivate the context if we fail to fill_info
Avoid leaving the context activated if we end up erroring out.

Similar to https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3492

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:50 +02:00
Guillaume Desmottes b3e33e329b textahead: add settings to display previous buffers
I'll use this in Karapulse to keep displaying the few previous lyrics
rather than having them disappear right away.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:44 +02:00
Sebastian Dröge 8c27aefe76 net: Update to async-tungstenite 0.19
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:38 +02:00
Sebastian Dröge 44ec9eba7f audiorrnoise: Use correct value range for the samples
The nnnoiseless crate wants all samples in the range [-32767,32767]
instead of the [-1,1] range we're using for floating point samples.

Scale before/after processing while (de)interleaving the samples.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/276

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:34 +02:00
Sebastian Dröge fd5b31fb43 tttocea608: Don't fail if a gap event contains no duration
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:27 +02:00
Sebastian Dröge d79edce517 webrtchttp: Fix documentation JSON
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:21 +02:00
Sebastian Dröge 412c191fc2 whipsink: Handle offer creation errors more gracefully
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:16 +02:00
Sebastian Dröge e46d2dfa54 webrtchttp: Fix missing import for docs build
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:10 +02:00
Sebastian Dröge e4788662b9 webrtchttp: Don't use let-else for now
We still support Rust 1.63.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:39:04 +02:00
Sebastian Dröge cab5410782 webrtchttp: Fix formatting
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:59 +02:00
Sanchayan Maity 8ac5632561 webrtchttp: Use tokio runtime for spawning thread used for candidate offer
While at it, we had a bug in whepsrc where for redirect we were
incorrectly calling initial_post_request instead of do_post. Fix
that.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:53 +02:00
Sanchayan Maity 4f67623c22 webrtchttp: Use a proper Rust type name for ICE transport policy
We don't need to namespace here but can just use the Rust namespaces.
Only the GType name has to stay like it is.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:48 +02:00
Sanchayan Maity 1d4d9b3bdb webrtchttp: Do not import element_imp_error
element_imp_error and such macros should not be imported but rather
only be accessed via gst namespace.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:41 +02:00
Sanchayan Maity 3202c4dc39 webrtchttp: Do not block webrtcbin signal handlers for sending candidates
While at it, drop the OPTIONS request in WHIP sink. This was not really
required. See section 4.4 of the spec
https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html#name-stun-turn-server-configurat

Also introduce a new error type and distinguish between a future being
aborted or returning an error.

We call abort only during shutdown and hence except for the DELETE
resource request being aborted, other waits on future should not
be fatal.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:36 +02:00
Alba Mendez 3bc9df7e71 webrtchttp: whipsink: construct TURN URL correctly
Right now the code manually pieces together the components
in a String for efficiency. When credentials contain special
characters this can result in invalid URLs, so do it the proper
way (with Url::parse + format) to make sure components are escaped
as needed.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:29 +02:00
Sanchayan Maity 929c48e19a webrtchttp: Drop unused dependencies
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:38:20 +02:00
Sanchayan Maity 4e9ec324e1 webrtchttp: Implement timeout for waiting on futures
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:47 +02:00
Sanchayan Maity 3fc0326084 webrtchttp: whipsink: Add candidates when sending the offer
WHIP endpoint providers like Cloudflare do not support Trickle ICE
and need candidates to be send along with the initial offer. Instead
of sending the offer in create-offer promise, send it once the ICE
candidates have been gathered.

While at it add properties to set STUN and TURN server along with the
ICE transport policy as at least when testing the Cloudflare WHIP
endpoint seems unreachable without it. This has also been observed
with Cloudflare provided demos.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:43 +02:00
Sanchayan Maity 420716fb63 webrtchttp: whipsink: Miscellaneous clean up
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:35 +02:00
Sanchayan Maity baf3da86cc webrtchttp: Factor out the common bits for WHIP and WHEP
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:30 +02:00
Sanchayan Maity 2e529fa152 Add a WebRTC WHEP source element
This implements WHEP specification based on
https://datatracker.ietf.org/doc/html/draft-murillo-whep-00

and has been tested with Cloudflare.

Server offers are likely to be removed from the WHEP specification
in upcoming revisions, to avoid compatibility issues. None of the
commercial services implementing WHEP support server initiated offers.
So we only support client side initiated offers.

Follows session setup and tear down as covered in Figure 1, Section 3
of the specification.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:13 +02:00
Sebastian Dröge 60772d2c06 ci: Disable gst-build job for now
See https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/262

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:04 +02:00
Raphael Dürscheid 184f879bf7 webrtcsink: Support nvv4l2vp9enc
Naive support for nvv4l2vp9enc by assuming configuration is equivalent
to existing nvv4l2vp8enc. Validated to have relevant properties.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:37:00 +02:00
Sebastian Dröge b2ad89cf06 deny: Remove another dependency that is not duplicated anymore
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:36:50 +02:00
Seungha Yang 506c96e8aa dav1ddec: Lower rank to primary
The rank of AOM av1dec was demoted as secondary, and thus
primary rank is sufficient.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:36:43 +02:00
Sebastian Dröge 2227b41342 deny: Remove dependencies that are not duplicated anymore
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:36:38 +02:00
Sebastian Dröge e6789fc338 rav1e: Update to rav1e 0.6
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:36:26 +02:00
Sebastian Dröge b7534643be gtk4: example: Use a bin with a videoconvert in the non-GL case
The sink only supports RGB formats in that case, which decoders rarely
would output.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:35:52 +02:00
Sebastian Dröge 0b2aa2646f gtk4: Make GL support fully optional
Don't depend on gstreamer-gl if it's not enabled, and don't try doing
anything with the GDK GL context at all.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:35:07 +02:00
Jordan Petridis 507377c052 video/gtk4: Implement support for GLTextures when possible.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:34:22 +02:00
Jordan Petridis f590b7e62f video/gtk4: Restrict visibility of struct related to the Frame
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:32:32 +02:00
Jordan Petridis 9fa3d88a63 video/gtk4: Rename Object types and struct to something simpler
Avoid the confusion caused by SinkPaintable and PaintableSink,
and instead refer to the objects as Paintable for the GdkPaintable
subclass or PaintalbeSink for the gst element.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:32:25 +02:00
Jordan Petridis b8d2d98027 mux/{mp4, fmp4}: Hard depend on feature v1_18
Else --no-default-features was failing to compile.

v1_18 is needed to for the aggregator code.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:32:02 +02:00
Jordan Petridis bfe62488f4 net/ndi: fix build with --no-default-features
doc_show_default() is only available with gst/v1_18

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:30:57 +02:00
Jordan Petridis 922f14ea19 meson: Fix build of static plugins
While we were correctly skipping the plugins that couldn't be
built statically, we were still adding their names to the list
and the .pc list causing them to still get built.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:30:48 +02:00
Jordan Petridis 204e9af663 meson: Fix build of static plugins
While we were correctly skipping the plugins that couldn't be
built statically, we were still adding their names to the list
and the .pc list causing them to still get built.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:30:42 +02:00
Sebastian Dröge a5f48507c4 textwrap: Don't panic on empty buffers
Simply don't calculate with any duration per word for this buffer.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:30:34 +02:00
Sebastian Dröge 7df114e0e9 deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1008>
2022-12-12 13:30:23 +02:00
Sebastian Dröge a7c75f8066 fmp4mux: Crank clock for the first fragment in more tests
Due to how aggregator works, it depends on how buffers are pulled
whether aggregate() is called again or it is waiting for a timeout or EOS:

works:
  - pad 1: 4 buffers, pad 2: 4 buffers
  - aggregate ready: take all 4/4 buffers
  - pad 1: 1 buffers, pad 2: 1 buffer
  - aggregate ready: take all 1/1 buffers

waits:
  - pad 1: 5 buffers, pad 2: 4 buffers
  - aggregate ready: take all 5/4 buffers
  - pad 1: 0 buffers, pad 2: 1 buffer
  - aggregate not ready: waiting for timeout or EOS

Also don't manually set the clock time as that's unnecessary.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/274

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/999>
2022-12-05 00:47:51 +00:00
Tim-Philipp Müller 08799d242c Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/999>
2022-12-05 00:30:48 +00:00
Vivia Nikolaidou a59a0340cf ndisrc: Use actual number of channels in positions_from_mask
Otherwise it fails for mono and stereo

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/992>
2022-11-29 12:19:26 +02:00
Vivia Nikolaidou cadf36ff01 ndisrc: Use default channel mask for audio output
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/989>
2022-11-28 17:10:08 +02:00
Sebastian Dröge 6a05b7f56a mp4: Add version to gst-plugin-version-helper dependency 2022-11-28 11:46:29 +02:00
Sebastian Dröge 1f4a035dc0 Update versions to 0.9.2 2022-11-28 11:44:33 +02:00
Sebastian Dröge b41d1e3f34 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:14 +02:00
Sebastian Dröge 649434bd04 mp4mux: For video with N/1001 framerates use N as timescale
See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3049

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge 10813ed621 mp4mux: Factor out running time to UTC time calculation into a function
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge ea0d5751a2 mp4mux: Remove unnecessary error case of negative PTS when doing the ONVIF UTC time calculations
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge c771c86631 mp4mux: Skip gap buffers instead of writing empty samples
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge 516b561191 mp4: Add ONVIF non-fragmented MP4 muxer
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge 23e8fea170 mp4: Remove unneeded cast in tests
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge 81a46ee33d mp4: Update to url 2
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge e7f5e73e3f gst-plugin-mp4: Add new MP4 plugin with a non-fragmented MP4 muxer
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/987>
2022-11-28 10:47:02 +02:00
Sebastian Dröge 969be7ab52 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:53:40 +02:00
Sebastian Dröge 931917e559 aws: Update to env_logger 0.10 for the tests
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:53:13 +02:00
Sebastian Dröge 1fb0062059 deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:52:39 +02:00
Sebastian Dröge 93ba677b18 fmp4mux: Handle EOS correctly if it happens before a fragment start time was determined
Whatever earliest time we have at that point is going to be the start
time.

Also handle the case correctly where all inputs are EOS before any
buffers were received at all.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/270

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:52:30 +02:00
Sebastian Dröge 9491c77540 fmp4mux: For video with N/1001 framerates use N as timescale
See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3049

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:51:48 +02:00
Sebastian Dröge 6c15bba592 fmp4mux: Re-work buffer dequeueing and calculations of timestamps
Especially simplify calculation of ONVIF UTC times. As a side-effect
this reduces the number of times the running times of a buffer are
calculated, and also causes streams to be interleaved correctly in ONVIF
mode if there is a non-constant UTC-to-running-time difference.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:51:48 +02:00
Sebastian Dröge 2b287bcd61 gif: Update to gif 0.12
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:45:18 +02:00
Sebastian Dröge 582cc34895 Provide explicit type to Iterator::sum() calls to avoid ambiguity
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:44:44 +02:00
Guillaume Desmottes 8bd9de8d48 spotify: fix "start a runtime from within a runtime" with static link
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:44:23 +02:00
Arun Raghavan b015688447 aws: s3sink: Treat stopping without EOS as an error for multipart upload
This allows us to try to clean up based on configuration (abort /
complete / do nothing) if the pipeline is shut down without an EOS.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/986>
2022-11-27 20:44:15 +02:00
Sebastian Dröge 274e57a536 Update Cargo.lock 2022-11-13 20:26:21 +02:00
Sebastian Dröge e434fd19ca Update versions to 0.9.1 2022-11-13 20:23:47 +02:00
Sebastian Dröge 28065de413 closedcaption: Update for deprecated chrono functions
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 18:41:58 +02:00
Sebastian Dröge a6f64b5b20 version-helper: Update for deprecated chrono functions
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 18:24:02 +02:00
Sebastian Dröge 5295fe9e67 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 18:15:50 +02:00
Guillaume Desmottes 331d053516 webrtc: README: fix couple of links
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Mathieu Duponchelle 5c9bc03eab webrtcsink: improve debug
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Tim-Philipp Müller 8c454c5c37 ci: add trigger job and only run documentation job post-merge
- require manual trigger to run pipeline on branches and MRs
- require manual trigger to run pipeline post-merge (excl. docs)

https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/417

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Tim-Philipp Müller a9f3ff2925 ci: add integration stage and move documentation job to that
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
François Laignel cdf07dd860 ts/udpsink: handle items in the PadSinkHandler
... instead of forwarding them to a Task via a channel.

This improves CPU usage by 5% according to `udpsrc-benchmark-sender`
with the `tuning` feature using default audio test buffers and
400 streams on the same ts-context.

It is expected to improve latency significantly. This is inferred
from `ts-standalone`: latency shrinks from around 5ms to 1.5µs
using the `task` sink compared to the `async-mutex` sink.

The async Mutex is mandatory here as we need to hold the lock
across await points.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
François Laignel 2e52fece61 ts: introduce ts-audiotestsrc
This makes it easy to generate "listenable" signals and to evaluate
discontinuities.

When the `tuning` feature is activated and the `main-elem` property
is set, the element can log the parked duration in %, which is an
image of the CPU usage for the ts-context.

This commit adds a test mode to `udpsrc-benchmark-sender` which
generates default audio buffers from `ts-audiotestsrc`. The `rtp`
mode is modified so that it uses `ts-audiotestsrc`.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
François Laignel 01816e2a8a ts/standalone: add new Sinks
Contrary to the existing Task Sink, the Async and Sync Mutex Sinks
handle buffers in the `PadSinkHandler` directly. The Async Mutex
Sink uses an async Mutex for the `PadSinkHandlerInner` while the
Sync Mutex Sink uses... a sync Mutex.

All Sinks share the same settings and stats manager.

Use the `--sink` command line option to select the sink (default is
`sync-mutex` since it allows evaluating the framework with as little
overhead as possible.

Also apply various fixes:

- Only keep the segment start instead of the full `Segment`. This
  helps with cache locality (`Segment` is a plain struct with many
  fields) and avoids downcasting the generic `Segment` upon each
  buffer handling.
- Box the `Stat`s. This should improve cache locality a bit.
- Fix EOS handling which took ages for no benefits in this
  particular use case.
- Use a macro to raise log level in the main element.
- Move error handling during item processing in `handle_loop_error`.
  This function was precisely designed for this and it should reduce
  the `handle_item`'s Future size.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
François Laignel ea82881e1c ts/standalone: move current sink under task_sink
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Sebastian Dröge 429e545e5c deny: Update
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Sebastian Dröge 2e3373647a Add missing doc features to WebRTC plugins
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Sebastian Dröge 43ac186e69 fmp4mux: Make media/trak timescales configurable
And refactor a bit of code for easier extensibility.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Sebastian Dröge 1ef9a46508 ci: Update to cargo-c 0.9.14
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:50 +00:00
Jan Beich b7891e77e5 meson: optionalize pango dependency used by net/onvif
Similar to -Dpango=<auto|enabled|disabled> in gst-plugins-base.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Jan Beich bdb423e2b9 ndi: provide Unix fallback after 3fe9e4a207
error[E0425]: cannot find value `LIBRARY_NAME` in this scope
   --> net/ndi/src/ndisys.rs:336:23
    |
336 |             path.push(LIBRARY_NAME);
    |                       ^^^^^^^^^^^^ not found in this scope

error[E0425]: cannot find value `LIBRARY_NAME` in this scope
   --> net/ndi/src/ndisys.rs:339:33
    |
339 |             path::PathBuf::from(LIBRARY_NAME)
    |                                 ^^^^^^^^^^^^ not found in this scope

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge 4556657602 fmp4mux: Don't allow VP9 for CMAF
This would require setting the correct compatible band for VP9 in CMAF,
which is not implemented yet.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge cb5a956ee7 fmp4mux: Add initial Opus support
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/239

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge d0228ed544 docs: Remove some stale entries of renamed elements
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Arun Raghavan 6b3f0f764e aws: Skip s3 test on Windows until we figure out why it times out
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge 07f3b0f504 Fix various new clippy warnings
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge 8dc22d3bf1 fmp4mux: For VP9, write resolution into the tkhd and include a stss box to signal that not all frames are sync samples
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge f2f0eb30e0 webrtc: Update to human_bytes 0.4
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge b596b407f6 aws: Update to aws 0.21/0.51
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge 2b6d87cf66 fmp4mux: Remove unused uuid dependency
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/977>
2022-11-12 15:52:49 +00:00
Sebastian Dröge 18fa678a5c Update Cargo.lock 2022-10-27 15:36:30 +03:00
Sebastian Dröge 8e2a6500aa fmp4mux: Clip negative PTS to zero/last PTS instead of erroring out
This can happen at the beginning of a stream if upstream is
rtpjitterbuffer and it has problems figuring out timestamps in the
beginning due to resetting / skew.
2022-10-27 15:35:17 +03:00
Sebastian Dröge e268577994 fmp4mux: Send force-keyunit events for now if the ideal position has already passed 2022-10-27 15:35:17 +03:00
Sebastian Dröge f2a6a8d3de fmp4mux: Add debug log when writing the mfra box 2022-10-27 15:35:17 +03:00
Sebastian Dröge 51ff099221 fmp4mux: Reset timing infos to None if a stream only contained gap events for a whole fragment 2022-10-27 15:35:17 +03:00
Sebastian Dröge eefa8540ba fmp4mux: If a stream is longer than the main stream at EOS, simply include all of its buffers in the last fragment nonetheless 2022-10-27 15:35:17 +03:00
Sebastian Dröge 790453364d whipsink: Add object to debug logs 2022-10-27 15:34:52 +03:00
Matthew Waters 32d2372e90 fmp4mux: don't require dts for predictive-only formats like vp9 2022-10-27 15:34:52 +03:00
Guillaume Desmottes 15955758b6 aws: fix title in README
The title was not matching the actual plugin name which was confusing.
2022-10-27 15:34:52 +03:00
Sebastian Dröge 54fe3f1c02 deny: Update 2022-10-27 15:34:32 +03:00
Matthew Waters a54318fbb4 fmp4: add support for muxing VP9 streams in cmaf, dash and iso fmp4
As specified in https://www.webmproject.org/vp9/mp4/
2022-10-27 15:34:32 +03:00
Sebastian Dröge 46152533ba Add Cargo.lock 2022-10-24 19:28:41 +03:00
Sebastian Dröge ba5270d30a Update to release versions of gtk-rs and gstreamer-rs 2022-10-24 19:28:41 +03:00
Sebastian Dröge 2ff40142db Update versions to 0.9.0 2022-10-24 18:25:05 +03:00
279 changed files with 41536 additions and 6539 deletions

View file

@ -6,7 +6,7 @@ include:
file: '/templates/debian.yml'
- project: 'gstreamer/gstreamer-rs'
ref: main
ref: '0.19'
file: '/ci/images_template.yml'
- project: 'gstreamer/gstreamer'
@ -38,10 +38,33 @@ default:
interruptible: true
stages:
- "trigger"
- "prep"
- "lint"
- "test"
- "extras"
- "integration"
# This is an empty job that is used to trigger the pipeline.
trigger:
image: alpine:latest
stage: 'trigger'
variables:
GIT_STRATEGY: none
script:
- echo "Trigger job done, now running the pipeline."
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
# If the MR is assigned to the Merge bot, trigger the pipeline automatically
- if: '$CI_MERGE_REQUEST_ASSIGNEES == "gstreamer-merge-bot"'
# Require explicit action to trigger tests post merge
- if: '$CI_PROJECT_NAMESPACE == "gstreamer" && $CI_COMMIT_BRANCH == "main"'
when: 'manual'
# When the assignee isn't the merge bot, require an explicit action to trigger the pipeline
# to avoid wasting CI resources
- if: '$CI_MERGE_REQUEST_ASSIGNEES != "gstreamer-merge-bot"'
when: 'manual'
allow_failure: false
.debian:11:
variables:
@ -49,30 +72,23 @@ stages:
before_script:
- source ./ci/env.sh
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config
# If cargo exists assume we probably will want to update
# the lockfile
- |
if command -v cargo; then
cargo generate-lockfile
cargo update
fi
.debian:11-stable:
extends: .debian:11
variables:
FDO_DISTRIBUTION_TAG: '$GST_RS_STABLE-${GST_RS_IMG_TAG}_2022-09-07.0'
FDO_DISTRIBUTION_TAG: '$GST_RS_STABLE-${GST_RS_IMG_TAG}_2022-11-05.0'
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:$GST_RS_STABLE-$GST_RS_IMG_TAG"
.debian:11-msrv:
extends: .debian:11
variables:
FDO_DISTRIBUTION_TAG: '$GST_RS_MSRV-${GST_RS_IMG_TAG}_2022-09-07.0'
FDO_DISTRIBUTION_TAG: '$GST_RS_MSRV-${GST_RS_IMG_TAG}_2022-11-05.0'
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:$GST_RS_MSRV-$GST_RS_IMG_TAG"
.debian:11-nightly:
extends: .debian:11
variables:
FDO_DISTRIBUTION_TAG: 'nightly-${GST_RS_IMG_TAG}_2022-09-07.0'
FDO_DISTRIBUTION_TAG: 'nightly-${GST_RS_IMG_TAG}_2022-11-05.0'
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:nightly-$GST_RS_IMG_TAG"
.build-debian-container:
@ -86,6 +102,8 @@ stages:
apt clean &&
bash ./ci/install-rust-ext.sh &&
pip install tomli
needs:
- "trigger"
rules:
- if: '$UPDATE_IMG == null'
@ -230,7 +248,7 @@ meson shared:
meson static:
extends: .meson
script:
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium=built-in
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium-source=built-in
- ninja -C build install
- ./ci/generate-static-test.py test-static-link-all
- cd test-static-link-all
@ -246,10 +264,10 @@ meson static:
# Check that the gstreamer documentation keeps working
documentation:
image: $GSTREAMER_DOC_IMAGE
stage: 'integration'
variables:
MESON_ARGS: >
-Ddoc=enabled
-Domx=disabled
-Dpython=disabled
-Dlibav=disabled
-Dlibnice=disabled
@ -258,7 +276,7 @@ documentation:
-Dsharp=disabled
-Dgst-examples=disabled
-Drs=enabled
-Dgst-plugins-rs:sodium=system
-Dgst-plugins-rs:sodium-source=system
-Dgst-docs:fatal_warnings=true
-Dorc=disabled
script:
@ -279,54 +297,65 @@ documentation:
paths:
- documentation/
needs: []
rules:
# Run job if the MR is assigned to the Merge bot or it a post-merge pipeline on main branch
- if: '$CI_MERGE_REQUEST_ASSIGNEES == "gstreamer-merge-bot"'
when: 'always'
- if: '$CI_PROJECT_NAMESPACE == "gstreamer" && $CI_COMMIT_BRANCH == "main"'
when: 'always'
# Require explicit action to trigger otherwise
- if: '$CI_PROJECT_NAMESPACE != "gstreamer" || $CI_COMMIT_BRANCH != "main"'
when: 'manual'
# build gst-plugins-rs as a gst-build subproject
gst-build:
extends: .meson
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: 'manual'
allow_failure: true
variables:
MESON_ARGS: >
-Domx=disabled
-Dpython=disabled
-Dlibav=disabled
-Dlibnice=disabled
-Dugly=disabled
-Dbad=disabled
-Ddevtools=disabled
-Dges=disabled
-Drtsp_server=disabled
-Dvaapi=disabled
-Dsharp=disabled
-Dgst-examples=disabled
-Drs=enabled
-Dgst-plugins-rs:sodium=system
script:
- P=$(pwd)
- cd ..
- rm -rf gstreamer
- git clone --depth 1 https://gitlab.freedesktop.org/gstreamer/gstreamer.git --branch main
- cd gstreamer
- ln -s $P subprojects/gst-plugins-rs
- meson build $MESON_ARGS
- ninja -C build
# Check static Rust plugins can be linked into gst-full
- meson build-gst-full --default-library=static $MESON_ARGS
- ninja -C build-gst-full
- meson devenv -C build-gst-full ./gst-inspect-1.0 rsaudiofx
artifacts:
expire_in: '7 days'
when: always
paths:
- 'build/meson-logs/'
- 'build-gst-full/meson-logs/'
# Disabled because of https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/262
#gst-build:
# extends: .meson
# rules:
# - if: '$CI_PIPELINE_SOURCE == "schedule"'
# - if: $CI_PIPELINE_SOURCE == "merge_request_event"
# when: 'manual'
# allow_failure: true
# variables:
# MESON_ARGS: >
# -Domx=disabled
# -Dpython=disabled
# -Dlibav=disabled
# -Dlibnice=disabled
# -Dugly=disabled
# -Dbad=disabled
# -Ddevtools=disabled
# -Dges=disabled
# -Drtsp_server=disabled
# -Dvaapi=disabled
# -Dsharp=disabled
# -Dgst-examples=disabled
# -Drs=enabled
# -Dgst-plugins-rs:sodium-source=system
# script:
# - P=$(pwd)
# - cd ..
# - rm -rf gstreamer
# - git clone --depth 1 https://gitlab.freedesktop.org/gstreamer/gstreamer.git --branch main
# - cd gstreamer
# - ln -s $P subprojects/gst-plugins-rs
# - meson build $MESON_ARGS
# - ninja -C build
# # Check static Rust plugins can be linked into gst-full
# - meson build-gst-full --default-library=static $MESON_ARGS
# - ninja -C build-gst-full
# - meson devenv -C build-gst-full ./gst-inspect-1.0 rsaudiofx
# artifacts:
# expire_in: '7 days'
# when: always
# paths:
# - 'build/meson-logs/'
# - 'build-gst-full/meson-logs/'
.msvc2019 build:
stage: 'test'
needs: []
needs:
- 'trigger'
tags:
- 'docker'
- 'windows'

6471
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -16,6 +16,7 @@ members = [
"mux/flavors",
"mux/fmp4",
"mux/mp4",
"net/aws",
"net/hlssink3",
@ -35,6 +36,7 @@ members = [
"text/wrap",
"utils/fallbackswitch",
"utils/livesync",
"utils/togglerecord",
"utils/tracers",
"utils/uriplaylistbin",
@ -63,6 +65,7 @@ default-members = [
"generic/threadshare",
"mux/fmp4",
"mux/mp4",
"net/aws",
"net/hlssink3",
@ -82,6 +85,7 @@ default-members = [
"text/wrap",
"utils/fallbackswitch",
"utils/livesync",
"utils/togglerecord",
"utils/tracers",
"utils/uriplaylistbin",

View file

@ -110,6 +110,8 @@ You will find the following plugins in this repository:
- `fmp4`: A fragmented MP4/ISOBMFF/CMAF muxer for generating e.g. DASH/HLS media fragments.
- `mp4`: A non-fragmented MP4 muxer for generating MP4 files.
* `text`
- `ahead`: A plugin to display upcoming text buffers ahead.
@ -127,6 +129,9 @@ You will find the following plugins in this repository:
configuring a fallback audio/video if there are problems with the main
source.
- `livesync`: Element to maintain a continuous live stream from a
potentially unstable source.
- `togglerecord`: Element to enable starting and stopping multiple streams together.
- `tracers`: Plugin with multiple tracers:

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-audiofx"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
@ -9,9 +9,9 @@ edition = "2021"
rust-version = "1.63"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1", features = ["v1_16"] }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19", features = ["v1_16"] }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19", features = ["v1_16"] }
anyhow = "1"
byte-slice-cast = "1.0"
num-traits = "0.2"
@ -29,11 +29,11 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[dev-dependencies]
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_18"] }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19", features = ["v1_18"] }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-MPL-2.0

View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -90,10 +90,10 @@ fn run() -> Result<(), Error> {
let new_z = -x * f32::sin(ROTATION) + z * f32::cos(ROTATION);
let objs = [gst::Structure::builder("application/spatial-object")
.field("x", &new_x)
.field("y", &y)
.field("z", &new_z)
.field("distance-gain", &gain)
.field("x", new_x)
.field("y", y)
.field("z", new_z)
.field("distance-gain", gain)
.build()];
hrtf.set_property("spatial-objects", gst::Array::new(objs));

View file

@ -15,8 +15,8 @@ pub struct RingBuffer {
impl RingBuffer {
pub fn new(size: usize) -> Self {
let mut buffer = Vec::with_capacity(size as usize);
buffer.extend(iter::repeat(0.0).take(size as usize));
let mut buffer = Vec::with_capacity(size);
buffer.extend(iter::repeat(0.0).take(size));
Self {
buffer: buffer.into_boxed_slice(),

View file

@ -611,10 +611,9 @@ impl State {
// the position where we have to start writing the next 100ms in the next
// iteration.
let mut outbuf = gst::Buffer::with_size(
self.current_samples_per_frame as usize * self.info.bpf() as usize,
)
.map_err(|_| gst::FlowError::Error)?;
let mut outbuf =
gst::Buffer::with_size(self.current_samples_per_frame * self.info.bpf() as usize)
.map_err(|_| gst::FlowError::Error)?;
{
let outbuf = outbuf.get_mut().unwrap();
let mut dst = outbuf.map_writable().map_err(|_| gst::FlowError::Error)?;
@ -819,7 +818,7 @@ impl State {
// adjustment. frame_type should only ever be set to Final at the end if we ended up in
// Inner state before.
if self.frame_type == FrameType::First
&& (src.len() / self.info.channels() as usize) < self.current_samples_per_frame as usize
&& (src.len() / self.info.channels() as usize) < self.current_samples_per_frame
{
self.process_first_frame_is_last(imp)?;
}
@ -1560,7 +1559,7 @@ impl AudioLoudNorm {
}
// Need to reset the state now
*state = State::new(&*self.settings.lock().unwrap(), state.info.clone());
*state = State::new(&self.settings.lock().unwrap(), state.info.clone());
}
state.adapter.push(buffer);
@ -1602,7 +1601,7 @@ impl AudioLoudNorm {
Err(_) => return false,
};
}
*state = Some(State::new(&*self.settings.lock().unwrap(), info));
*state = Some(State::new(&self.settings.lock().unwrap(), info));
drop(state);
if let Some(outbuf) = outbuf {
@ -1623,7 +1622,7 @@ impl AudioLoudNorm {
Err(gst::FlowError::Eos) => None,
Err(_) => return false,
};
*state = State::new(&*self.settings.lock().unwrap(), state.info.clone());
*state = State::new(&self.settings.lock().unwrap(), state.info.clone());
}
drop(state);

View file

@ -7,6 +7,8 @@
//
// SPDX-License-Identifier: MPL-2.0
use std::sync::Mutex;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
@ -31,8 +33,22 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
)
});
const DEFAULT_VOICE_ACTIVITY_THRESHOLD: f32 = 0.0;
const FRAME_SIZE: usize = DenoiseState::FRAME_SIZE;
#[derive(Debug, Clone, Copy)]
struct Settings {
vad_threshold: f32,
}
impl Default for Settings {
fn default() -> Self {
Settings {
vad_threshold: DEFAULT_VOICE_ACTIVITY_THRESHOLD,
}
}
}
struct ChannelDenoiser {
denoiser: Box<DenoiseState<'static>>,
frame_chunk: Box<[f32; FRAME_SIZE]>,
@ -47,6 +63,7 @@ struct State {
#[derive(Default)]
pub struct AudioRNNoise {
settings: Mutex<Settings>,
state: AtomicRefCell<Option<State>>,
}
@ -82,43 +99,6 @@ impl State {
fn needs_more_data(&self) -> bool {
self.adapter.available() < (FRAME_SIZE * self.in_info.bpf() as usize)
}
fn process(&mut self, input_plane: &[f32], output_plane: &mut [f32]) {
let channels = self.in_info.channels() as usize;
let size = FRAME_SIZE * channels;
for (out_frame, in_frame) in output_plane.chunks_mut(size).zip(input_plane.chunks(size)) {
for (index, item) in in_frame.iter().enumerate() {
let channel_index = index % channels;
let channel_denoiser = &mut self.denoisers[channel_index];
let pos = index / channels;
channel_denoiser.frame_chunk[pos] = *item;
}
for i in (in_frame.len() / channels)..(size / channels) {
for c in 0..channels {
let channel_denoiser = &mut self.denoisers[c];
channel_denoiser.frame_chunk[i] = 0.0;
}
}
// FIXME: The first chunks coming out of the denoisers contains some
// fade-in artifacts. We might want to discard those.
for channel_denoiser in &mut self.denoisers {
channel_denoiser.denoiser.process_frame(
&mut channel_denoiser.out_chunk[..],
&channel_denoiser.frame_chunk[..],
);
}
for (index, item) in out_frame.iter_mut().enumerate() {
let channel_index = index % channels;
let channel_denoiser = &self.denoisers[channel_index];
let pos = index / channels;
*item = channel_denoiser.out_chunk[pos];
}
}
}
}
impl AudioRNNoise {
@ -131,6 +111,7 @@ impl AudioRNNoise {
return Ok(gst::FlowSuccess::Ok);
}
let settings = *self.settings.lock().unwrap();
let mut buffer = gst::Buffer::with_size(available).map_err(|e| {
gst::error!(CAT, imp: self, "Failed to allocate buffer at EOS {:?}", e);
gst::FlowError::Flushing
@ -151,7 +132,7 @@ impl AudioRNNoise {
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
state.process(in_data, out_data);
self.process(state, &settings, in_data, out_data);
}
self.obj().src_pad().push(buffer)
@ -164,6 +145,7 @@ impl AudioRNNoise {
let duration = state.buffer_duration(output_size as _);
let pts = state.current_pts();
let settings = *self.settings.lock().unwrap();
let mut buffer = gst::Buffer::with_size(output_size).map_err(|_| gst::FlowError::Error)?;
{
@ -181,11 +163,64 @@ impl AudioRNNoise {
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
state.process(in_data, out_data);
self.process(state, &settings, in_data, out_data);
}
Ok(GenerateOutputSuccess::Buffer(buffer))
}
fn process(
&self,
state: &mut State,
settings: &Settings,
input_plane: &[f32],
output_plane: &mut [f32],
) {
let channels = state.in_info.channels() as usize;
let size = FRAME_SIZE * channels;
for (out_frame, in_frame) in output_plane.chunks_mut(size).zip(input_plane.chunks(size)) {
for (index, item) in in_frame.iter().enumerate() {
let channel_index = index % channels;
let channel_denoiser = &mut state.denoisers[channel_index];
let pos = index / channels;
channel_denoiser.frame_chunk[pos] = *item * 32767.0;
}
for i in (in_frame.len() / channels)..(size / channels) {
for c in 0..channels {
let channel_denoiser = &mut state.denoisers[c];
channel_denoiser.frame_chunk[i] = 0.0;
}
}
// FIXME: The first chunks coming out of the denoisers contains some
// fade-in artifacts. We might want to discard those.
let mut vad: f32 = 0.0;
for channel_denoiser in &mut state.denoisers {
vad = f32::max(
vad,
channel_denoiser.denoiser.process_frame(
&mut channel_denoiser.out_chunk[..],
&channel_denoiser.frame_chunk[..],
),
);
}
gst::debug!(CAT, imp: self, "Voice activity: {}", vad);
if vad < settings.vad_threshold {
out_frame.fill(0.0);
} else {
for (index, item) in out_frame.iter_mut().enumerate() {
let channel_index = index % channels;
let channel_denoiser = &state.denoisers[channel_index];
let pos = index / channels;
*item = channel_denoiser.out_chunk[pos] / 32767.0;
}
}
}
}
}
#[glib::object_subclass]
@ -195,7 +230,42 @@ impl ObjectSubclass for AudioRNNoise {
type ParentType = gst_base::BaseTransform;
}
impl ObjectImpl for AudioRNNoise {}
impl ObjectImpl for AudioRNNoise {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecFloat::builder("voice-activity-threshold")
.nick("Voice activity threshold")
.blurb("Threshold of the voice activity detector below which to mute the output")
.minimum(0.0)
.maximum(1.0)
.default_value(DEFAULT_VOICE_ACTIVITY_THRESHOLD)
.mutable_playing()
.build()]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"voice-activity-threshold" => {
let mut settings = self.settings.lock().unwrap();
settings.vad_threshold = value.get().expect("type checked upstream");
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"voice-activity-threshold" => {
let settings = self.settings.lock().unwrap();
settings.vad_threshold.to_value()
}
_ => unimplemented!(),
}
}
}
impl GstObjectImpl for AudioRNNoise {}

View file

@ -138,7 +138,7 @@ impl ObjectImpl for EbuR128Level {
.build()]
});
&*SIGNALS
&SIGNALS
}
fn properties() -> &'static [glib::ParamSpec] {
@ -482,7 +482,7 @@ impl BaseTransformImpl for EbuR128Level {
if state.ebur128.mode().contains(ebur128::Mode::M) {
match state.ebur128.loudness_momentary() {
Ok(loudness) => s.set("momentary-loudness", &loudness),
Ok(loudness) => s.set("momentary-loudness", loudness),
Err(err) => gst::error!(
CAT,
imp: self,
@ -494,7 +494,7 @@ impl BaseTransformImpl for EbuR128Level {
if state.ebur128.mode().contains(ebur128::Mode::S) {
match state.ebur128.loudness_shortterm() {
Ok(loudness) => s.set("shortterm-loudness", &loudness),
Ok(loudness) => s.set("shortterm-loudness", loudness),
Err(err) => gst::error!(
CAT,
imp: self,
@ -506,7 +506,7 @@ impl BaseTransformImpl for EbuR128Level {
if state.ebur128.mode().contains(ebur128::Mode::I) {
match state.ebur128.loudness_global() {
Ok(loudness) => s.set("global-loudness", &loudness),
Ok(loudness) => s.set("global-loudness", loudness),
Err(err) => gst::error!(
CAT,
imp: self,
@ -516,7 +516,7 @@ impl BaseTransformImpl for EbuR128Level {
}
match state.ebur128.relative_threshold() {
Ok(threshold) => s.set("relative-threshold", &threshold),
Ok(threshold) => s.set("relative-threshold", threshold),
Err(err) => gst::error!(
CAT,
imp: self,
@ -528,7 +528,7 @@ impl BaseTransformImpl for EbuR128Level {
if state.ebur128.mode().contains(ebur128::Mode::LRA) {
match state.ebur128.loudness_range() {
Ok(range) => s.set("loudness-range", &range),
Ok(range) => s.set("loudness-range", range),
Err(err) => {
gst::error!(CAT, imp: self, "Failed to get loudness range: {}", err)
}

View file

@ -373,7 +373,7 @@ impl HrtfRender {
let (prev_offset, _) = state.adapter.prev_offset();
let offset = prev_offset.checked_add(distance_samples).unwrap_or(0);
let duration_samples = outputsz / outbpf as usize;
let duration_samples = outputsz / outbpf;
let duration = samples_to_time(duration_samples as u64);
(pts, offset, duration)

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-claxon"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Ruben Gonzalez <rgonzalez@fluendo.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
@ -9,15 +9,15 @@ edition = "2021"
rust-version = "1.63"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
claxon = { version = "0.4" }
byte-slice-cast = "1.0"
atomic_refcell = "0.1"
once_cell = "1"
[dev-dependencies]
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
[lib]
name = "gstclaxon"
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-APACHE

201
audio/claxon/LICENSE-APACHE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1 +0,0 @@
../../LICENSE-MIT

23
audio/claxon/LICENSE-MIT Normal file
View file

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-csound"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Natanael Mojica <neithanmo@gmail.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
@ -9,15 +9,15 @@ rust-version = "1.63"
description = "GStreamer Audio Filter plugin based on Csound"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
csound = "0.1.8"
once_cell = "1.0"
byte-slice-cast = "1.0"
[dev-dependencies]
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
[lib]
name = "gstcsound"
@ -29,7 +29,7 @@ name = "csound-effect"
path = "examples/effect_example.rs"
[build-dependencies]
gst-plugin-version-helper = { path = "../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path = "../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-MPL-2.0

View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -80,7 +80,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Box<dyn Error>> {
let audio_sink = gst::parse_bin_from_description(AUDIO_SINK, true)?.upcast();
let csoundfilter = gst::ElementFactory::make("csoundfilter")
.property("csd-text", &CSD)
.property("csd-text", CSD)
.build()
.unwrap();

View file

@ -517,15 +517,15 @@ impl BaseTransformImpl for CsoundFilter {
let ichannels = csound.input_channels() as i32;
let ochannels = csound.output_channels() as i32;
for s in new_caps.make_mut().iter_mut() {
s.set("format", &gst_audio::AUDIO_FORMAT_F64.to_str());
s.set("rate", &sr);
s.set("format", gst_audio::AUDIO_FORMAT_F64.to_str());
s.set("rate", sr);
// replace the channel property with our values,
// if they are not supported, the negotiation will fail.
if direction == gst::PadDirection::Src {
s.set("channels", &ichannels);
s.set("channels", ichannels);
} else {
s.set("channels", &ochannels);
s.set("channels", ochannels);
}
// Csound does not have a concept of channel-mask
s.remove_field("channel-mask");

View file

@ -57,7 +57,7 @@ fn init() {
fn build_harness(src_caps: gst::Caps, sink_caps: gst::Caps, csd: &str) -> gst_check::Harness {
let filter = gst::ElementFactory::make("csoundfilter")
.property("csd-text", &csd)
.property("csd-text", csd)
.build()
.unwrap();
@ -260,10 +260,7 @@ fn csound_filter_underflow() {
}
assert_eq!(num_buffers, UNDERFLOW_NUM_BUFFERS / 2);
assert_eq!(
num_samples as usize,
UNDERFLOW_NUM_SAMPLES * UNDERFLOW_NUM_BUFFERS
);
assert_eq!(num_samples, UNDERFLOW_NUM_SAMPLES * UNDERFLOW_NUM_BUFFERS);
}
// Verifies that the caps negotiation is properly done, by pushing buffers whose caps

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-lewton"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
@ -9,15 +9,15 @@ edition = "2021"
rust-version = "1.63"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
lewton = { version = "0.10", default-features = false }
byte-slice-cast = "1.0"
atomic_refcell = "0.1"
once_cell = "1.0"
[dev-dependencies]
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
[lib]
name = "gstlewton"
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-APACHE

201
audio/lewton/LICENSE-APACHE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1 +0,0 @@
../../LICENSE-MIT

23
audio/lewton/LICENSE-MIT Normal file
View file

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -405,7 +405,7 @@ impl LewtonDec {
let outbuf = if let Some(ref reorder_map) = state.reorder_map {
let mut outbuf = self
.obj()
.allocate_output_buffer(sample_count as usize * audio_info.bpf() as usize);
.allocate_output_buffer(sample_count * audio_info.bpf() as usize);
{
// And copy the decoded data into our output buffer while reordering the channels to the
// GStreamer channel order

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-spotify"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Guillaume Desmottes <guillaume@desmottes.be>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
@ -9,8 +9,8 @@ edition = "2021"
rust-version = "1.63"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
once_cell = "1.0"
librespot = { version = "0.4", default-features = false }
tokio = "1.0"
@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-MPL-2.0

View file

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -6,9 +6,10 @@
//
// SPDX-License-Identifier: MPL-2.0
use std::sync::{mpsc, Arc, Mutex};
use std::sync::{mpsc, Arc, Mutex, MutexGuard};
use anyhow::bail;
use futures::future::{AbortHandle, Abortable, Aborted};
use once_cell::sync::Lazy;
use tokio::{runtime, task::JoinHandle};
@ -30,6 +31,8 @@ use librespot::playback::{
player::{Player, PlayerEvent},
};
use super::Bitrate;
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"spotifyaudiosrc",
@ -70,14 +73,21 @@ struct Settings {
cache_files: String,
cache_max_size: u64,
track: String,
bitrate: Bitrate,
}
#[derive(Default)]
pub struct SpotifyAudioSrc {
setup_thread: Mutex<Option<SetupThread>>,
state: Arc<Mutex<Option<State>>>,
settings: Mutex<Settings>,
}
struct SetupThread {
thread_handle: std::thread::JoinHandle<Result<anyhow::Result<()>, Aborted>>,
abort_handle: AbortHandle,
}
#[glib::object_subclass]
impl ObjectSubclass for SpotifyAudioSrc {
const NAME: &'static str = "GstSpotifyAudioSrc";
@ -125,6 +135,11 @@ impl ObjectImpl for SpotifyAudioSrc {
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecEnum::builder("bitrate", Bitrate::default())
.nick("Spotify bitrate")
.blurb("Spotify audio bitrate in kbit/s")
.mutable_ready()
.build()
]
});
@ -157,6 +172,10 @@ impl ObjectImpl for SpotifyAudioSrc {
let mut settings = self.settings.lock().unwrap();
settings.track = value.get().expect("type checked upstream");
}
"bitrate" => {
let mut settings = self.settings.lock().unwrap();
settings.bitrate = value.get().expect("type checked upstream");
}
_ => unimplemented!(),
}
}
@ -187,6 +206,10 @@ impl ObjectImpl for SpotifyAudioSrc {
let settings = self.settings.lock().unwrap();
settings.track.to_value()
}
"bitrate" => {
let settings = self.settings.lock().unwrap();
settings.bitrate.to_value()
}
_ => unimplemented!(),
}
}
@ -237,17 +260,22 @@ impl BaseSrcImpl for SpotifyAudioSrc {
}
}
if let Err(err) = RUNTIME.block_on(async move { self.setup().await }) {
let details = format!("{:?}", err);
gst::error!(CAT, imp: self, "failed to start: {}", details);
gst::element_imp_error!(self, gst::ResourceError::Settings, [&details]);
return Err(gst::error_msg!(gst::ResourceError::Settings, [&details]));
{
let setup_thread = self.setup_thread.lock().unwrap();
if setup_thread.is_some() {
// already starting
return Ok(());
}
self.start_setup(setup_thread);
}
Ok(())
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
// stop the setup if it's not completed yet
self.cancel_setup();
if let Some(state) = self.state.lock().unwrap().take() {
gst::debug!(CAT, imp: self, "stopping");
state.player.stop();
@ -258,6 +286,12 @@ impl BaseSrcImpl for SpotifyAudioSrc {
Ok(())
}
fn unlock(&self) -> Result<(), gst::ErrorMessage> {
self.cancel_setup();
self.parent_unlock()
}
}
impl PushSrcImpl for SpotifyAudioSrc {
@ -265,6 +299,41 @@ impl PushSrcImpl for SpotifyAudioSrc {
&self,
_buffer: Option<&mut gst::BufferRef>,
) -> Result<CreateSuccess, gst::FlowError> {
let state_set = {
let state = self.state.lock().unwrap();
state.is_some()
};
if !state_set {
let setup_thread = self.setup_thread.lock().unwrap();
if setup_thread.is_none() {
// unlock() could potentially cancel the setup, and create() can be called after unlock() without going through start() again.
self.start_setup(setup_thread);
}
}
{
// wait for the setup to be completed
let mut setup_thread = self.setup_thread.lock().unwrap();
if let Some(setup) = setup_thread.take() {
let res = setup.thread_handle.join().unwrap();
match res {
Err(_aborted) => {
gst::debug!(CAT, imp: self, "setup has been cancelled");
return Err(gst::FlowError::Flushing);
}
Ok(Err(err)) => {
let details = format!("{:?}", err);
gst::error!(CAT, imp: self, "failed to start: {}", details);
gst::element_imp_error!(self, gst::ResourceError::Settings, [&details]);
return Err(gst::FlowError::Error);
}
Ok(Ok(_)) => {}
}
}
}
let state = self.state.lock().unwrap();
let state = state.as_ref().unwrap();
@ -290,112 +359,6 @@ impl PushSrcImpl for SpotifyAudioSrc {
}
}
impl SpotifyAudioSrc {
async fn setup(&self) -> anyhow::Result<()> {
let (credentials, cache, track) = {
let settings = self.settings.lock().unwrap();
let credentials_cache = if settings.cache_credentials.is_empty() {
None
} else {
Some(&settings.cache_credentials)
};
let files_cache = if settings.cache_files.is_empty() {
None
} else {
Some(&settings.cache_files)
};
let max_size = if settings.cache_max_size != 0 {
Some(settings.cache_max_size)
} else {
None
};
let cache = Cache::new(credentials_cache, None, files_cache, max_size)?;
let credentials = match cache.credentials() {
Some(cached_cred) => {
gst::debug!(CAT, imp: self, "reuse credentials from cache",);
cached_cred
}
None => {
gst::debug!(CAT, imp: self, "credentials not in cache",);
if settings.username.is_empty() {
bail!("username is not set and credentials are not in cache");
}
if settings.password.is_empty() {
bail!("password is not set and credentials are not in cache");
}
let cred = Credentials::with_password(&settings.username, &settings.password);
cache.save_credentials(&cred);
cred
}
};
if settings.track.is_empty() {
bail!("track is not set")
}
(credentials, cache, settings.track.clone())
};
let state = self.state.clone();
let (session, _credentials) =
Session::connect(SessionConfig::default(), credentials, Some(cache), false).await?;
let player_config = PlayerConfig {
passthrough: true,
..Default::default()
};
// use a sync channel to prevent buffering the whole track inside the channel
let (sender, receiver) = mpsc::sync_channel(2);
let sender_clone = sender.clone();
let (mut player, mut player_event_channel) =
Player::new(player_config, session, Box::new(NoOpVolume), || {
Box::new(BufferSink { sender })
});
let track = match SpotifyId::from_uri(&track) {
Ok(track) => track,
Err(_) => bail!("Failed to create Spotify URI from track"),
};
player.load(track, true, 0);
let player_channel_handle = RUNTIME.spawn(async move {
let sender = sender_clone;
while let Some(event) = player_event_channel.recv().await {
match event {
PlayerEvent::EndOfTrack { .. } => {
let _ = sender.send(Message::Eos);
}
PlayerEvent::Unavailable { .. } => {
let _ = sender.send(Message::Unavailable);
}
_ => {}
}
}
});
let mut state = state.lock().unwrap();
state.replace(State {
player,
receiver,
player_channel_handle,
});
Ok(())
}
}
struct BufferSink {
sender: mpsc::SyncSender<Message>,
}
@ -456,3 +419,149 @@ impl URIHandlerImpl for SpotifyAudioSrc {
Ok(())
}
}
impl SpotifyAudioSrc {
fn start_setup(&self, mut setup_thread: MutexGuard<Option<SetupThread>>) {
let self_ = self.to_owned();
// run the runtime from another thread to prevent the "start a runtime from within a runtime" panic
// when the plugin is statically linked.
let (abort_handle, abort_registration) = AbortHandle::new_pair();
let thread_handle = std::thread::spawn(move || {
RUNTIME.block_on(async move {
let future = Abortable::new(self_.setup(), abort_registration);
future.await
})
});
setup_thread.replace(SetupThread {
thread_handle,
abort_handle,
});
}
async fn setup(&self) -> anyhow::Result<()> {
{
let state = self.state.lock().unwrap();
if state.is_some() {
// already setup
return Ok(());
}
}
let (credentials, cache, track, bitrate) = {
let settings = self.settings.lock().unwrap();
let credentials_cache = if settings.cache_credentials.is_empty() {
None
} else {
Some(&settings.cache_credentials)
};
let files_cache = if settings.cache_files.is_empty() {
None
} else {
Some(&settings.cache_files)
};
let max_size = if settings.cache_max_size != 0 {
Some(settings.cache_max_size)
} else {
None
};
let cache = Cache::new(credentials_cache, None, files_cache, max_size)?;
let credentials = match cache.credentials() {
Some(cached_cred)
if settings.username.is_empty()
|| settings.username == cached_cred.username =>
{
gst::debug!(CAT, imp: self, "reuse credentials from cache",);
cached_cred
}
_ => {
gst::debug!(CAT, imp: self, "credentials not in cache",);
if settings.username.is_empty() {
bail!("username is not set and credentials are not in cache");
}
if settings.password.is_empty() {
bail!("password is not set and credentials are not in cache");
}
Credentials::with_password(&settings.username, &settings.password)
}
};
if settings.track.is_empty() {
bail!("track is not set")
}
let bitrate = settings.bitrate.into();
gst::debug!(CAT, imp: self, "Requesting bitrate {:?}", bitrate);
(credentials, cache, settings.track.clone(), bitrate)
};
let (session, _credentials) =
Session::connect(SessionConfig::default(), credentials, Some(cache), true).await?;
let player_config = PlayerConfig {
passthrough: true,
bitrate,
..Default::default()
};
// use a sync channel to prevent buffering the whole track inside the channel
let (sender, receiver) = mpsc::sync_channel(2);
let sender_clone = sender.clone();
let (mut player, mut player_event_channel) =
Player::new(player_config, session, Box::new(NoOpVolume), || {
Box::new(BufferSink { sender })
});
let track = match SpotifyId::from_uri(&track) {
Ok(track) => track,
Err(_) => bail!("Failed to create Spotify URI from track"),
};
player.load(track, true, 0);
let player_channel_handle = RUNTIME.spawn(async move {
let sender = sender_clone;
while let Some(event) = player_event_channel.recv().await {
match event {
PlayerEvent::EndOfTrack { .. } => {
let _ = sender.send(Message::Eos);
}
PlayerEvent::Unavailable { .. } => {
let _ = sender.send(Message::Unavailable);
}
_ => {}
}
}
});
let mut state = self.state.lock().unwrap();
state.replace(State {
player,
receiver,
player_channel_handle,
});
Ok(())
}
fn cancel_setup(&self) {
let mut setup_thread = self.setup_thread.lock().unwrap();
if let Some(setup) = setup_thread.take() {
setup.abort_handle.abort();
}
}
}

View file

@ -11,11 +11,42 @@ use gst::prelude::*;
mod imp;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
#[repr(u32)]
#[enum_type(name = "GstRsSpotifyBitrate")]
enum Bitrate {
#[enum_value(name = "96 kbit/s", nick = "96")]
B96,
#[enum_value(name = "160 kbit/s", nick = "160")]
B160,
#[enum_value(name = "320 kbit/s", nick = "320")]
B320,
}
impl Default for Bitrate {
fn default() -> Self {
Self::B160
}
}
impl From<Bitrate> for librespot::playback::config::Bitrate {
fn from(value: Bitrate) -> Self {
match value {
Bitrate::B96 => Self::Bitrate96,
Bitrate::B160 => Self::Bitrate160,
Bitrate::B320 => Self::Bitrate320,
}
}
}
glib::wrapper! {
pub struct SpotifyAudioSrc(ObjectSubclass<imp::SpotifyAudioSrc>) @extends gst_base::PushSrc, gst_base::BaseSrc, gst::Element, gst::Object, @implements gst::URIHandler;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
#[cfg(feature = "doc")]
Bitrate::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
gst::Element::register(
Some(plugin),
"spotifyaudiosrc",

View file

@ -7,6 +7,7 @@ import os
import shutil
import subprocess
import sys
import shlex
from argparse import ArgumentParser
from pathlib import Path as P
@ -16,24 +17,44 @@ PARSER.add_argument('build_dir', type=P)
PARSER.add_argument('src_dir', type=P)
PARSER.add_argument('root_dir', type=P)
PARSER.add_argument('target', choices=['release', 'debug'])
PARSER.add_argument('include')
PARSER.add_argument('extra_env')
PARSER.add_argument('prefix', type=P)
PARSER.add_argument('libdir', type=P)
PARSER.add_argument('--version', default=None)
PARSER.add_argument('--bin', default=None, type=P)
PARSER.add_argument('--exts', nargs="+", default=[])
PARSER.add_argument('--features', nargs="+", default=[])
PARSER.add_argument('--packages', nargs="+", default=[])
PARSER.add_argument('--examples', nargs="+", default=[])
PARSER.add_argument('--lib-suffixes', nargs="+", default=[])
PARSER.add_argument('--exe-suffix')
PARSER.add_argument('--depfile')
PARSER.add_argument('--disable-doc', action="store_true", default=False)
def shlex_join(args):
if hasattr(shlex, 'join'):
return shlex.join(args)
return ' '.join([shlex.quote(arg) for arg in args])
def generate_depfile_for(fpath):
file_stem = fpath.parent / fpath.stem
depfile_content = ""
with open(f"{file_stem}.d", 'r') as depfile:
for l in depfile.readlines():
if l.startswith(str(file_stem)):
output, srcs = l.split(":", maxsplit=2)
# We can't blindly split on `:` because on Windows that's part
# of the drive letter. Lucky for us, the format of the dep file
# is one of:
#
# /path/to/output: /path/to/src1 /path/to/src2
# /path/to/output:
#
# So we parse these two cases specifically
if l.endswith(':'):
output = l[:-1]
srcs = ''
else:
output, srcs = l.split(": ", maxsplit=2)
all_deps = []
for src in srcs.split(" "):
@ -53,33 +74,45 @@ def generate_depfile_for(fpath):
if __name__ == "__main__":
opts = PARSER.parse_args()
logfile = open(opts.root_dir / 'meson-logs' /
f'{opts.src_dir.name}-cargo-wrapper.log', 'w')
logdir = opts.root_dir / 'meson-logs'
logfile_path = logdir / f'{opts.src_dir.name}-cargo-wrapper.log'
logfile = open(logfile_path, mode='w', buffering=1)
print(opts, file=logfile)
cargo_target_dir = opts.build_dir / 'target'
env = os.environ.copy()
env['CARGO_TARGET_DIR'] = str(cargo_target_dir)
pkg_config_path = env.get('PKG_CONFIG_PATH', '').split(':')
if 'PKG_CONFIG_PATH' in env:
pkg_config_path = env['PKG_CONFIG_PATH'].split(os.pathsep)
else:
pkg_config_path = []
pkg_config_path.append(str(opts.root_dir / 'meson-uninstalled'))
env['PKG_CONFIG_PATH'] = ':'.join(pkg_config_path)
env['PKG_CONFIG_PATH'] = os.pathsep.join(pkg_config_path)
if opts.extra_env:
for e in opts.extra_env.split(','):
k, v = e.split(':')
env[k] = v
if 'NASM' in env:
env['PATH'] = os.pathsep.join([os.path.dirname(env['NASM']), env['PATH']])
rustc_target = None
if 'RUSTC' in env:
rustc_cmdline = shlex.split(env['RUSTC'])
# grab target from RUSTFLAGS
rust_flags = rustc_cmdline[1:] + shlex.split(env.get('RUSTFLAGS', ''))
if '--target' in rust_flags:
rustc_target_idx = rust_flags.index('--target')
_ = rust_flags.pop(rustc_target_idx) # drop '--target'
rustc_target = rust_flags.pop(rustc_target_idx)
env['RUSTFLAGS'] = shlex_join(rust_flags)
env['RUSTC'] = rustc_cmdline[0]
features = opts.features
if opts.command == 'build':
cargo_cmd = ['cargo']
if opts.bin:
if opts.bin or opts.examples:
cargo_cmd += ['build']
else:
cargo_cmd += ['cbuild']
if not opts.disable_doc:
cargo_cmd += ['--features', "doc"]
features += ['doc']
if opts.target == 'release':
cargo_cmd.append('--release')
elif opts.command == 'test':
@ -89,40 +122,43 @@ if __name__ == "__main__":
print("Unknown command:", opts.command, file=logfile)
sys.exit(1)
cwd = None
if not opts.bin:
cargo_cmd.extend(['--manifest-path', opts.src_dir / 'Cargo.toml'])
cargo_cmd.extend(['--prefix', opts.prefix, '--libdir',
opts.prefix / opts.libdir])
for p in opts.include.split(','):
cargo_cmd.extend(['-p', p])
else:
if rustc_target:
cargo_cmd += ['--target', rustc_target]
if features:
cargo_cmd += ['--features', ','.join(features)]
cargo_cmd += ['--target-dir', cargo_target_dir]
cargo_cmd += ['--manifest-path', opts.src_dir / 'Cargo.toml']
if opts.bin:
cargo_cmd.extend(['--bin', opts.bin.name])
cwd = opts.src_dir
else:
if not opts.examples:
cargo_cmd.extend(['--prefix', opts.prefix, '--libdir',
opts.prefix / opts.libdir])
for p in opts.packages:
cargo_cmd.extend(['-p', p])
for e in opts.examples:
cargo_cmd.extend(['--example', e])
def run(cargo_cmd, env, cwd=cwd):
def run(cargo_cmd, env):
print(cargo_cmd, env, file=logfile)
try:
subprocess.run(cargo_cmd, env=env, check=True, cwd=cwd)
subprocess.run(cargo_cmd, env=env, cwd=opts.src_dir, check=True)
except subprocess.SubprocessError:
sys.exit(1)
run(cargo_cmd, env, cwd)
run(cargo_cmd, env)
if opts.command == 'build':
target_dir = cargo_target_dir / '**' / opts.target
if opts.bin:
if opts.exts[0]:
ext = f'.{opts.exts[0]}'
else:
ext = ''
exe = glob.glob(str(target_dir / opts.bin) + ext, recursive=True)[0]
exe = glob.glob(str(target_dir / opts.bin) + opts.exe_suffix, recursive=True)[0]
shutil.copy2(exe, opts.build_dir)
depfile_content = generate_depfile_for(P(exe))
else:
# Copy so files to build dir
depfile_content = ""
for ext in opts.exts:
for f in glob.glob(str(target_dir / f'*.{ext}'), recursive=True):
for suffix in opts.lib_suffixes:
for f in glob.glob(str(target_dir / f'*.{suffix}'), recursive=True):
libfile = P(f)
depfile_content += generate_depfile_for(libfile)
@ -137,6 +173,12 @@ if __name__ == "__main__":
print(f"Copying {copied_file}", file=logfile)
shutil.copy2(f, opts.build_dir)
# Copy examples to builddir
for example in opts.examples:
example_glob = str(target_dir / 'examples' / example) + opts.exe_suffix
exe = glob.glob(example_glob, recursive=True)[0]
shutil.copy2(exe, opts.build_dir)
depfile_content += generate_depfile_for(P(exe))
with open(opts.depfile, 'w') as depfile:
depfile.write(depfile_content)

View file

@ -7,7 +7,8 @@ import os
from utils import iterate_plugins
# the csound version used on ci does not ship a .pc file
IGNORE = ['csound']
# threadshare we skip in meson static build as well
IGNORE = ['csound', 'threadshare', 'gtk4']
outdir = sys.argv[1]

View file

@ -3,4 +3,4 @@ source ./ci/env.sh
set -e
export CARGO_HOME='/usr/local/cargo'
cargo install cargo-c --version 0.9.12+cargo-0.64
cargo install cargo-c --version 0.9.14+cargo-0.66

View file

@ -17,11 +17,18 @@ function Run-Tests {
param (
$Features
)
$local_exclude = $exclude_crates;
# In this case the plugin will pull x11/wayland features
# which will fail to build on windows.
if (($Features -eq '--all-features') -or ($Features -eq '')) {
$local_exclude += @("--exclude", "gst-plugin-gtk4")
}
Write-Host "Features: $Features"
Write-Host "Exclude string: $exclude_crates"
Write-Host "Exclude string: $local_exclude"
cargo build --color=always --workspace $exclude_crates --all-targets $Features
cargo build --color=always --workspace $local_exclude --all-targets $Features
if (!$?) {
Write-Host "Build failed"
@ -29,7 +36,7 @@ function Run-Tests {
}
$env:G_DEBUG="fatal_warnings"
cargo test --no-fail-fast --color=always --workspace $exclude_crates --all-targets $Features
cargo test --no-fail-fast --color=always --workspace $local_exclude --all-targets $Features
if (!$?) {
Write-Host "Tests failed"

117
deny.toml
View file

@ -9,6 +9,7 @@ ignore = [
"RUSTSEC-2021-0059",
"RUSTSEC-2021-0060",
"RUSTSEC-2021-0061",
"RUSTSEC-2021-0145",
# https://github.com/chronotope/chrono/issues/499
"RUSTSEC-2020-0071",
# sodiumoxide is deprecated
@ -55,12 +56,6 @@ wildcards = "allow"
name = "time"
version = "0.1"
# ignore duplicated textwrap dependency because clap depends on an old version
# https://github.com/clap-rs/clap/pull/1994
[[bans.skip]]
name = "textwrap"
version = "0.11"
# ignore duplicated rustc_version dependency because rav1e depends on an old version
[[bans.skip]]
name = "rustc_version"
@ -69,31 +64,13 @@ version = "0.3"
name = "semver"
version = "0.11"
# ignore duplicated system-deps dependency because dav1d depends on an old version
[[bans.skip]]
name = "system-deps"
version = "3"
[[bans.skip]]
name = "version-compare"
version = "0.0"
[[bans.skip]]
name = "cfg-expr"
version = "0.7"
# ignore duplicated crc dependency because ffv1 depends on an old version
# https://github.com/rust-av/ffv1/issues/21
[[bans.skip]]
name = "crc"
version = "1.8"
# ignore duplicated heck dependency because various crates depend on an old version
[[bans.skip]]
name = "heck"
version = "0.3"
# ignore duplicated sha-1/digest/block-buffer dependencies because librespot depends on an old version
# Ignore various duplicated dependencies because librespot depends on an old versions
[[bans.skip]]
name = "block-buffer"
version = "0.9"
@ -103,6 +80,12 @@ version = "0.9"
[[bans.skip]]
name = "sha-1"
version = "0.9"
[[bans.skip]]
name = "env_logger"
version = "0.9"
[[bans.skip]]
name = "hmac"
version = "0.11"
# ignore duplicated wasi dependency because various crates depends on an old version
[[bans.skip]]
@ -114,6 +97,90 @@ version = "0.10"
name = "spin"
version = "0.5"
# cookie_store depends on older idna
# https://github.com/pfernie/cookie_store/commit/b9c710f45550c5c8997f18a83e6fcc5998cf1726
[[bans.skip]]
name = "idna"
version = "0.2"
# field-offset and nix depend on an older memoffset
# https://github.com/Diggsey/rust-field-offset/pull/23
# https://github.com/nix-rust/nix/pull/1885
[[bans.skip]]
name = "memoffset"
version = "0.6"
# Various crates depend on an older version of hermit-abi
[[bans.skip]]
name = "hermit-abi"
version = "0.1"
[[bans.skip]]
name = "hermit-abi"
version = "0.2"
# Various crates depend on an older version of base64
[[bans.skip]]
name = "base64"
version = "0.13"
# Various crates depend on an older version of windows-sys
[[bans.skip]]
name = "windows-sys"
version = "0.42"
[[bans.skip]]
name = "windows-sys"
version = "0.45"
[[bans.skip]]
name = "windows_x86_64_msvc"
version = "0.42"
[[bans.skip]]
name = "windows_x86_64_gnullvm"
version = "0.42"
[[bans.skip]]
name = "windows_x86_64_gnu"
version = "0.42"
[[bans.skip]]
name = "windows_i686_msvc"
version = "0.42"
[[bans.skip]]
name = "windows_i686_gnu"
version = "0.42"
[[bans.skip]]
name = "windows_aarch64_msvc"
version = "0.42"
[[bans.skip]]
name = "windows_aarch64_gnullvm"
version = "0.42"
[[bans.skip]]
name = "windows-targets"
version = "0.42"
# Various crates depend on an older version of socket2
[[bans.skip]]
name = "socket2"
version = "0.4"
# Various crates depend on an older version of syn
[[bans.skip]]
name = "syn"
version = "1.0"
# Various crates depend on an older version of bitflags
[[bans.skip]]
name = "bitflags"
version = "1.0"
# cargo-lock depends on an old version of the toml crate
# https://github.com/rustsec/rustsec/pull/805
[[bans.skip]]
name = "toml"
version = "0.5"
# Various crates depend on an older version of redox_syscall
[[bans.skip]]
name = "redox_syscall"
version = "0.2"
[sources]
unknown-registry = "deny"
unknown-git = "deny"

View file

@ -19,6 +19,8 @@ except ImportError:
PARSER = ArgumentParser()
PARSER.add_argument('src_dir', type=Path)
PARSER.add_argument('plugins', nargs='*')
PARSER.add_argument('--features', action="store_true", help="Get list of features to activate")
PARSER.add_argument('--gst-version', help="Get list of features to activate")
# Map plugin name to directory name, for those that does not match.
@ -38,27 +40,73 @@ RENAMES = {
'textwrap': 'wrap',
}
class CargoAnalyzer:
def __init__(self):
self.src_dir = None
self.plugins = None
self.features = False
self.gst_version = "1.18"
def extract_version(self, feature_name):
if feature_name.startswith('v'):
verindex = 1
elif feature_name.startswith('gst'):
verindex = 3
else:
return None
(majver, minver) = feature_name[verindex:].split("_")
return (int(majver), int(minver))
def extract_features(self, cargo_data):
features = cargo_data['features']
wanted_features = set()
gst_version_major = int(self.gst_version.split('.')[0])
gst_version_minor = int(self.gst_version.split('.')[1])
for (name, value) in features.items():
version = self.extract_version(name)
if version is None:
continue
(majver, minver) = version
if gst_version_major < majver or gst_version_minor < minver:
continue
wanted_features |= set(value)
if name.startswith("gst"):
# Required for some reason for rswebrtc which has a specific feature
wanted_features |= {f"{cargo_data['package']['name']}/{name}"}
return wanted_features
def run(self):
with (opts.src_dir / 'Cargo.toml').open('rb') as f:
crates = tomllib.load(f)['workspace']['members']
res = set()
for name in opts.plugins:
if name.startswith('gst'):
name = name[3:]
name = RENAMES.get(name, name)
crate_path = None
for crate in crates:
if Path(crate).name == name:
crate_path = opts.src_dir / crate / 'Cargo.toml'
assert crate_path
with crate_path.open('rb') as f:
data = tomllib.load(f)
if opts.features:
res |= self.extract_features(data)
else:
try:
requires = data['package']['metadata']['capi']['pkg_config']['requires_private']
except KeyError:
continue
res.update([i.strip().replace('>', "|>").replace('<', "|<").replace("==", "|==") for i in requires.split(',')])
return res
if __name__ == "__main__":
opts = PARSER.parse_args()
analyzer = CargoAnalyzer()
opts = PARSER.parse_args(namespace=analyzer)
with (opts.src_dir / 'Cargo.toml').open('rb') as f:
crates = tomllib.load(f)['workspace']['members']
deps = set()
for p in opts.plugins:
assert p.startswith('gst')
name = p[3:]
name = RENAMES.get(name, name)
crate_path = None
for crate in crates:
if Path(crate).name == name:
crate_path = opts.src_dir / crate / 'Cargo.toml'
assert crate_path
with crate_path.open('rb') as f:
data = tomllib.load(f)
try:
requires = data['package']['metadata']['capi']['pkg_config']['requires_private']
except KeyError:
continue
deps.update([i.strip().replace('>', "|>").replace('<', "|<").replace("==", "|==") for i in requires.split(',')])
print(','.join(deps))
print(','.join(analyzer.run()))

View file

@ -9,7 +9,7 @@ if meson.is_cross_build()
subdir_done()
endif
if static_build
if default_library == 'static'
if get_option('doc').enabled()
error('Documentation enabled but not supported when building statically.')
endif
@ -99,9 +99,17 @@ foreach plugin_name: list_plugin_res.stdout().split(':')
gst_index: 'plugins/index.md',
include_paths: join_paths(meson.current_source_dir(), '..'),
gst_smart_index: true,
gst_c_source_filters: [
'../target/*/*.rs',
'../target/*/*/*.rs',
'../target/*/*/*/*.rs',
'../target/*/*/*/*/*.rs',
'../target/*/*/*/*/*/*.rs',
],
gst_c_sources: [
'../*/*/*/*.rs',
'../*/*/*/*/*.rs',
'../*/*/*/*/*/*.rs',
],
dependencies: [gst_dep],
gst_order_generated_subpages: true,

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-file"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
@ -10,8 +10,8 @@ rust-version = "1.63"
[dependencies]
url = "2"
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
once_cell = "1.0"
[lib]
@ -20,7 +20,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-APACHE

201
generic/file/LICENSE-APACHE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1 +0,0 @@
../../LICENSE-MIT

23
generic/file/LICENSE-MIT Normal file
View file

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -74,7 +74,7 @@ impl FileLocation {
.parent()
.expect("FileSink::set_location `location` with filename but without a parent")
.to_owned();
if parent_dir.is_relative() && parent_dir.components().next() == None {
if parent_dir.is_relative() && parent_dir.components().next().is_none() {
// `location` only contains the filename
// need to specify "." for `canonicalize` to resolve the actual path
parent_dir = PathBuf::from(".");

View file

@ -13,6 +13,7 @@
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base::prelude::*;
use gst_base::subclass::prelude::*;
use std::fs::File;
@ -112,6 +113,12 @@ impl ObjectSubclass for FileSink {
}
impl ObjectImpl for FileSink {
fn constructed(&self) {
self.parent_constructed();
self.obj().set_sync(false);
}
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecString::builder("location")

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-sodium"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Jordan Petridis <jordan@centricular.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
description = "GStreamer plugin for libsodium-based file encryption and decryption"
@ -9,8 +9,8 @@ edition = "2021"
rust-version = "1.63"
[dependencies]
gst = { package="gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package="gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
sodiumoxide = "0.2.1"
once_cell = "1.3.0"
hex = "0.4"
@ -27,10 +27,14 @@ rand = "0.8"
[dev-dependencies.gst-check]
git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
branch = "0.19"
version = "0.19"
package="gstreamer-check"
[dev-dependencies.gst-app]
git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
branch = "0.19"
version = "0.19"
package="gstreamer-app"
[lib]
@ -54,7 +58,7 @@ path = "examples/decrypt_example.rs"
required-features = ["serde", "serde_json", "clap"]
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-MIT

View file

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -53,7 +53,7 @@ struct Keys {
impl Keys {
fn from_file(file: &Path) -> Result<Self, Box<dyn Error>> {
let f = File::open(&file)?;
let f = File::open(file)?;
serde_json::from_reader(f).map_err(From::from)
}
}

View file

@ -57,7 +57,7 @@ struct Keys {
impl Keys {
fn from_file(file: &Path) -> Result<Self, Box<dyn Error>> {
let f = File::open(&file)?;
let f = File::open(file)?;
serde_json::from_reader(f).map_err(From::from)
}
}

View file

@ -498,7 +498,7 @@ impl Decrypter {
gst::debug!(CAT, obj: pad, "Requested offset: {}", offset);
gst::debug!(CAT, obj: pad, "Requested size: {}", requested_size);
let chunk_index = offset as u64 / block_size as u64;
let chunk_index = offset / block_size as u64;
gst::debug!(CAT, obj: pad, "Stream Block index: {}", chunk_index);
let pull_offset = offset - (chunk_index * block_size as u64);

View file

@ -75,7 +75,7 @@ fn test_pipeline() {
};
let filesrc = gst::ElementFactory::make("filesrc")
.property("location", &input_path.to_str().unwrap())
.property("location", input_path.to_str().unwrap())
.build()
.unwrap();

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-threadshare"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
license = "LGPL-2.1-or-later"
description = "GStreamer Threadshare Plugin"
@ -10,20 +10,21 @@ rust-version = "1.63"
[dependencies]
async-task = "4.3.0"
concurrent-queue = "1.2.2"
concurrent-queue = "2"
flume = "0.10.13"
futures = "0.3.21"
libc = "0.2"
gio = { git = "https://github.com/gtk-rs/gtk-rs-core" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.16", version = "0.16" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
once_cell = "1"
pin-project-lite = "0.2.0"
polling = "2.2.0"
rand = "0.8"
slab = "0.4.7"
socket2 = {features = ["all"], version = "0.4"}
socket2 = {features = ["all"], version = "0.5"}
waker-fn = "1.1"
# Used by examples
@ -33,8 +34,8 @@ clap = { version = "4", features = ["derive"], optional = true }
winapi = { version = "0.3.9", features = ["winsock2", "processthreadsapi"] }
[dev-dependencies]
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
[lib]
name = "gstthreadshare"
@ -42,7 +43,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[[example]]
name = "benchmark"
name = "ts-benchmark"
path = "examples/benchmark.rs"
[[example]]
@ -58,7 +59,7 @@ name = "ts-standalone"
path = "examples/standalone/main.rs"
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
cc = "1.0.38"
pkg-config = "0.3.15"

View file

@ -1 +0,0 @@
../../LICENSE-LGPLv2

View file

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -63,7 +63,7 @@ fn main() {
};
let is_rtp = args.len() > 6 && (args[6] == "rtp");
let rtp_caps = gst::Caps::builder("audio/x-rtp")
let rtp_caps = gst::Caps::builder("application/x-rtp")
.field("media", "audio")
.field("payload", 8i32)
.field("clock-rate", 8000)
@ -97,7 +97,7 @@ fn main() {
"udpsrc" => {
let source = gst::ElementFactory::make("udpsrc")
.name(format!("source-{}", i).as_str())
.property("port", 40000i32 + i as i32)
.property("port", 5004i32 + i as i32)
.property("retrieve-sender-address", false)
.build()
.unwrap();
@ -108,7 +108,7 @@ fn main() {
let context = build_context();
let source = gst::ElementFactory::make("ts-udpsrc")
.name(format!("source-{}", i).as_str())
.property("port", 40000i32 + i as i32)
.property("port", 5004i32 + i as i32)
.property("context", &context)
.property("context-wait", wait)
.build()
@ -158,7 +158,7 @@ fn main() {
let context = build_context();
let source = gst::ElementFactory::make("ts-tonesrc")
.name(format!("source-{}", i).as_str())
.property("samples-per-buffer", (wait as u32) * 8000 / 1000)
.property("samples-per-buffer", wait * 8000 / 1000)
.property("context", &context)
.property("context-wait", wait)
.build()
@ -184,22 +184,7 @@ fn main() {
pipeline.add_many(elements).unwrap();
gst::Element::link_many(elements).unwrap();
} else {
let queue = if let Some(context) = context {
let queue = gst::ElementFactory::make("ts-queue")
.name(format!("queue-{}", i).as_str())
.property("context", &context)
.property("context-wait", wait)
.build()
.unwrap();
queue
} else {
gst::ElementFactory::make("queue")
.name(format!("queue-{}", i).as_str())
.build()
.unwrap()
};
let elements = &[&source, &queue, &sink];
let elements = &[&source, &sink];
pipeline.add_many(elements).unwrap();
gst::Element::link_many(elements).unwrap();
}
@ -268,14 +253,14 @@ fn main() {
let elapsed = init.elapsed();
gst::info!(
CAT,
"{:>6.2} / s / stream",
"Thrpt: {:>6.2}",
total_count * 1_000.0 / elapsed.as_millis() as f32
);
#[cfg(feature = "tuning")]
gst::info!(
CAT,
"{:>6.2}% parked",
"Parked: {:>6.2}%",
(ctx_0.parked_duration() - parked_init).as_nanos() as f32 * 100.0
/ elapsed.as_nanos() as f32
);

View file

@ -0,0 +1,66 @@
use super::super::CAT;
use clap::Parser;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
pub enum Sink {
/// Item handling in PadHandler with async Mutex
AsyncMutex,
/// Item handling in PadHandler with sync Mutex
SyncMutex,
/// Item handling in runtime::Task
Task,
}
impl Sink {
pub fn element_name(self) -> &'static str {
use super::super::sink;
use Sink::*;
match self {
AsyncMutex => sink::ASYNC_MUTEX_ELEMENT_NAME,
SyncMutex => sink::SYNC_MUTEX_ELEMENT_NAME,
Task => sink::TASK_ELEMENT_NAME,
}
}
}
#[derive(Parser, Debug)]
#[clap(version)]
#[clap(
about = "Standalone pipeline threadshare runtime test. Use `GST_DEBUG=ts-standalone*:4` for stats"
)]
pub struct Args {
/// Parallel streams to process.
#[clap(short, long, default_value_t = 5000)]
pub streams: u32,
/// Threadshare groups.
#[clap(short, long, default_value_t = 2)]
pub groups: u32,
/// Threadshare Context wait in ms (max throttling duration).
#[clap(short, long, default_value_t = 20)]
pub wait: u32,
/// Buffer push period in ms.
#[clap(short, long, default_value_t = 20)]
pub push_period: u32,
/// Number of buffers per stream to output before sending EOS (-1 = unlimited).
#[clap(short, long, default_value_t = 5000)]
pub num_buffers: i32,
/// The Sink variant to use.
#[clap(long, value_enum, default_value_t = Sink::SyncMutex)]
pub sink: Sink,
/// Disables statistics logging.
#[clap(short, long)]
pub disable_stats_log: bool,
}
pub fn args() -> Args {
let args = Args::parse();
gst::info!(CAT, "{:?}", args);
args
}

View file

@ -0,0 +1,47 @@
use super::super::CAT;
#[derive(Copy, Clone, Debug)]
pub struct SyncMutexSink;
impl SyncMutexSink {
pub fn element_name(self) -> &'static str {
super::super::sink::SYNC_MUTEX_ELEMENT_NAME
}
}
#[derive(Debug)]
pub struct Args {
pub streams: u32,
pub groups: u32,
pub wait: u32,
pub push_period: u32,
pub num_buffers: i32,
pub sink: SyncMutexSink,
pub disable_stats_log: bool,
}
impl Default for Args {
fn default() -> Self {
Args {
streams: 5000,
groups: 2,
wait: 20,
push_period: 20,
num_buffers: 5000,
sink: SyncMutexSink,
disable_stats_log: false,
}
}
}
pub fn args() -> Args {
if std::env::args().len() > 1 {
gst::warning!(CAT, "Ignoring command line arguments");
gst::warning!(CAT, "Build with `--features=clap`");
}
let args = Args::default();
gst::warning!(CAT, "{:?}", args);
args
}

View file

@ -0,0 +1,9 @@
#[cfg(not(feature = "clap"))]
mod default_args;
#[cfg(not(feature = "clap"))]
pub use default_args::*;
#[cfg(feature = "clap")]
mod clap_args;
#[cfg(feature = "clap")]
pub use clap_args::*;

View file

@ -0,0 +1,19 @@
macro_rules! debug_or_trace {
($cat:expr, $raise_log_level:expr, $qual:ident: $obj:expr, $rest:tt $(,)?) => {
if $raise_log_level {
gst::debug!($cat, $qual: $obj, $rest);
} else {
gst::trace!($cat, $qual: $obj, $rest);
}
};
}
macro_rules! log_or_trace {
($cat:expr, $raise_log_level:expr, $qual:ident: $obj:expr, $rest:tt $(,)?) => {
if $raise_log_level {
gst::log!($cat, $qual: $obj, $rest);
} else {
gst::trace!($cat, $qual: $obj, $rest);
}
};
}

View file

@ -1,12 +1,21 @@
use gst::glib;
use once_cell::sync::Lazy;
mod args;
use args::*;
#[macro_use]
mod macros;
mod sink;
mod src;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"ts-standalone-test-main",
"ts-standalone-main",
gst::DebugColorFlags::empty(),
Some("Thread-sharing standalone test main"),
)
@ -14,7 +23,9 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
src::register(plugin)?;
sink::register(plugin)?;
sink::async_mutex::register(plugin)?;
sink::sync_mutex::register(plugin)?;
sink::task::register(plugin)?;
Ok(())
}
@ -31,91 +42,6 @@ gst::plugin_define!(
env!("BUILD_REL_DATE")
);
#[cfg(feature = "clap")]
use clap::Parser;
#[cfg(feature = "clap")]
#[derive(Parser, Debug)]
#[clap(version)]
#[clap(
about = "Standalone pipeline threadshare runtime test. Use `GST_DEBUG=ts-standalone*:4` for stats"
)]
struct Args {
/// Parallel streams to process.
#[clap(short, long, default_value_t = 5000)]
streams: u32,
/// Threadshare groups.
#[clap(short, long, default_value_t = 2)]
groups: u32,
/// Threadshare Context wait in ms (max throttling duration).
#[clap(short, long, default_value_t = 20)]
wait: u32,
/// Buffer push period in ms.
#[clap(short, long, default_value_t = 20)]
push_period: u32,
/// Number of buffers per stream to output before sending EOS (-1 = unlimited).
#[clap(short, long, default_value_t = 5000)]
num_buffers: i32,
/// Disables statistics logging.
#[clap(short, long)]
disable_stats_log: bool,
}
#[cfg(not(feature = "clap"))]
#[derive(Debug)]
struct Args {
streams: u32,
groups: u32,
wait: u32,
push_period: u32,
num_buffers: i32,
disable_stats_log: bool,
}
#[cfg(not(feature = "clap"))]
impl Default for Args {
fn default() -> Self {
Args {
streams: 5000,
groups: 2,
wait: 20,
push_period: 20,
num_buffers: 5000,
disable_stats_log: false,
}
}
}
fn args() -> Args {
#[cfg(feature = "clap")]
let args = {
let args = Args::parse();
gst::info!(CAT, "{:?}", args);
args
};
#[cfg(not(feature = "clap"))]
let args = {
if std::env::args().len() > 1 {
gst::warning!(CAT, "Ignoring command line arguments");
gst::warning!(CAT, "Build with `--features=clap`");
}
let args = Args::default();
gst::warning!(CAT, "{:?}", args);
args
};
args
}
fn main() {
use gst::prelude::*;
use std::time::Instant;
@ -133,8 +59,8 @@ fn main() {
for i in 0..args.streams {
let ctx_name = format!("standalone {}", i % args.groups);
let src = gst::ElementFactory::make("ts-standalone-test-src")
.name(format!("src-{}", i).as_str())
let src = gst::ElementFactory::make(src::ELEMENT_NAME)
.name(format!("src-{i}").as_str())
.property("context", &ctx_name)
.property("context-wait", args.wait)
.property("push-period", args.push_period)
@ -142,16 +68,16 @@ fn main() {
.build()
.unwrap();
let sink = gst::ElementFactory::make("ts-standalone-test-sink")
.name(format!("sink-{}", i).as_str())
let sink = gst::ElementFactory::make(args.sink.element_name())
.name(format!("sink-{i}").as_str())
.property("context", &ctx_name)
.property("context-wait", args.wait)
.build()
.unwrap();
if i == 0 {
src.set_property("raise-log-level", true);
sink.set_property("raise-log-level", true);
src.set_property("main-elem", true);
sink.set_property("main-elem", true);
if !args.disable_stats_log {
// Don't use the last 5 secs in stats
@ -179,30 +105,46 @@ fn main() {
let l = glib::MainLoop::new(None, false);
let bus = pipeline.bus().unwrap();
let terminated_count = Arc::new(AtomicU32::new(0));
let pipeline_clone = pipeline.clone();
let l_clone = l.clone();
bus.add_watch(move |_, msg| {
use gst::MessageView;
match msg.view() {
MessageView::Eos(_) => {
// Actually, we don't post EOS (see sinks impl).
gst::info!(CAT, "Received eos");
l_clone.quit();
glib::Continue(false)
}
MessageView::Error(err) => {
MessageView::Error(msg) => {
if let gst::MessageView::Error(msg) = msg.message().view() {
if msg.error().matches(gst::LibraryError::Shutdown) {
if terminated_count.fetch_add(1, Ordering::SeqCst) == args.streams - 1 {
gst::info!(CAT, "Received all shutdown requests");
l_clone.quit();
return glib::Continue(false);
} else {
return glib::Continue(true);
}
}
}
gst::error!(
CAT,
"Error from {:?}: {} ({:?})",
err.src().map(|s| s.path_string()),
err.error(),
err.debug()
msg.src().map(|s| s.path_string()),
msg.error(),
msg.debug()
);
l_clone.quit();
}
_ => (),
};
glib::Continue(true)
glib::Continue(false)
}
_ => glib::Continue(true),
}
})
.expect("Failed to add bus watch");

View file

@ -0,0 +1,334 @@
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use futures::future::BoxFuture;
use futures::prelude::*;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst::EventView;
use once_cell::sync::Lazy;
use gstthreadshare::runtime::executor::block_on_or_add_sub_task;
use gstthreadshare::runtime::{prelude::*, PadSink};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use super::super::{Settings, Stats, CAT};
#[derive(Debug, Default)]
struct PadSinkHandlerInner {
is_flushing: bool,
is_main_elem: bool,
last_dts: Option<gst::ClockTime>,
segment_start: Option<gst::ClockTime>,
stats: Option<Box<Stats>>,
}
impl PadSinkHandlerInner {
fn handle_buffer(
&mut self,
elem: &super::AsyncMutexSink,
buffer: gst::Buffer,
) -> Result<(), gst::FlowError> {
if self.is_flushing {
log_or_trace!(
CAT,
self.is_main_elem,
obj: elem,
"Discarding {buffer:?} (flushing)"
);
return Err(gst::FlowError::Flushing);
}
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "Received {buffer:?}");
let dts = buffer
.dts()
.expect("Buffer without dts")
.checked_sub(self.segment_start.expect("Buffer without Time Segment"))
.expect("dts before Segment start");
if let Some(last_dts) = self.last_dts {
let cur_ts = elem.current_running_time().unwrap();
let latency: Duration = (cur_ts - dts).into();
let interval: Duration = (dts - last_dts).into();
if let Some(stats) = self.stats.as_mut() {
stats.add_buffer(latency, interval);
}
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "o latency {latency:.2?}");
debug_or_trace!(
CAT,
self.is_main_elem,
obj: elem,
"o interval {interval:.2?}",
);
}
self.last_dts = Some(dts);
log_or_trace!(CAT, self.is_main_elem, obj: elem, "Buffer processed");
Ok(())
}
}
#[derive(Clone, Debug, Default)]
struct AsyncPadSinkHandler(Arc<futures::lock::Mutex<PadSinkHandlerInner>>);
impl PadSinkHandler for AsyncPadSinkHandler {
type ElementImpl = AsyncMutexSink;
fn sink_chain(
self,
_pad: gst::Pad,
elem: super::AsyncMutexSink,
buffer: gst::Buffer,
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
async move {
if self.0.lock().await.handle_buffer(&elem, buffer).is_err() {
return Err(gst::FlowError::Flushing);
}
Ok(gst::FlowSuccess::Ok)
}
.boxed()
}
fn sink_event_serialized(
self,
_pad: gst::Pad,
elem: super::AsyncMutexSink,
event: gst::Event,
) -> BoxFuture<'static, bool> {
async move {
match event.view() {
EventView::Eos(_) => {
{
let mut inner = self.0.lock().await;
debug_or_trace!(CAT, inner.is_main_elem, obj: elem, "EOS");
inner.is_flushing = true;
}
// When each element sends its own EOS message,
// it takes ages for the pipeline to process all of them.
// Let's just post an error message and let main shuts down
// after all streams have posted this message.
let _ = elem
.post_message(gst::message::Error::new(gst::LibraryError::Shutdown, "EOS"));
}
EventView::FlushStop(_) => {
self.0.lock().await.is_flushing = false;
}
EventView::Segment(evt) => {
if let Some(time_seg) = evt.segment().downcast_ref::<gst::ClockTime>() {
self.0.lock().await.segment_start = time_seg.start();
}
}
EventView::SinkMessage(evt) => {
let _ = elem.post_message(evt.message());
}
_ => (),
}
true
}
.boxed()
}
fn sink_event(self, _pad: &gst::Pad, _imp: &AsyncMutexSink, event: gst::Event) -> bool {
if let EventView::FlushStart(..) = event.view() {
block_on_or_add_sub_task(async move { self.0.lock().await.is_flushing = true });
}
true
}
}
impl AsyncPadSinkHandler {
fn prepare(&self, is_main_elem: bool, stats: Option<Stats>) {
futures::executor::block_on(async move {
let mut inner = self.0.lock().await;
inner.is_main_elem = is_main_elem;
inner.stats = stats.map(Box::new);
});
}
fn start(&self) {
futures::executor::block_on(async move {
let mut inner = self.0.lock().await;
inner.is_flushing = false;
inner.last_dts = None;
if let Some(stats) = inner.stats.as_mut() {
stats.start();
}
});
}
fn stop(&self) {
futures::executor::block_on(async move {
let mut inner = self.0.lock().await;
inner.is_flushing = true;
});
}
}
#[derive(Debug)]
pub struct AsyncMutexSink {
sink_pad: PadSink,
sink_pad_handler: AsyncPadSinkHandler,
settings: Mutex<Settings>,
}
impl AsyncMutexSink {
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
let settings = self.settings.lock().unwrap();
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Preparing");
let stats = if settings.logs_stats {
Some(Stats::new(
settings.max_buffers,
settings.push_period + settings.context_wait / 2,
))
} else {
None
};
self.sink_pad_handler.prepare(settings.is_main_elem, stats);
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Prepared");
Ok(())
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
self.sink_pad_handler.stop();
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
Ok(())
}
fn start(&self) -> Result<(), gst::ErrorMessage> {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
self.sink_pad_handler.start();
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
Ok(())
}
}
#[glib::object_subclass]
impl ObjectSubclass for AsyncMutexSink {
const NAME: &'static str = "TsStandaloneAsyncMutexSink";
type Type = super::AsyncMutexSink;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
let sink_pad_handler = AsyncPadSinkHandler::default();
Self {
sink_pad: PadSink::new(
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
sink_pad_handler.clone(),
),
sink_pad_handler,
settings: Default::default(),
}
}
}
impl ObjectImpl for AsyncMutexSink {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(Settings::properties);
PROPERTIES.as_ref()
}
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
self.settings.lock().unwrap().set_property(id, value, pspec);
}
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.settings.lock().unwrap().property(id, pspec)
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
obj.set_element_flags(gst::ElementFlags::SINK);
}
}
impl GstObjectImpl for AsyncMutexSink {}
impl ElementImpl for AsyncMutexSink {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Thread-sharing standalone test async mutex sink",
"Sink/Test",
"Thread-sharing standalone test async mutex sink",
"François Laignel <fengalin@free.fr>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {transition:?}");
match transition {
gst::StateChange::NullToReady => {
self.prepare().map_err(|err| {
self.post_error_message(err);
gst::StateChangeError
})?;
}
gst::StateChange::ReadyToPaused => {
self.start().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::PausedToReady => {
self.stop().map_err(|_| gst::StateChangeError)?;
}
_ => (),
}
self.parent_change_state(transition)
}
}

View file

@ -0,0 +1,17 @@
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct AsyncMutexSink(ObjectSubclass<imp::AsyncMutexSink>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
super::ASYNC_MUTEX_ELEMENT_NAME,
gst::Rank::None,
AsyncMutexSink::static_type(),
)
}

View file

@ -1,864 +0,0 @@
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use futures::future::BoxFuture;
use futures::prelude::*;
use futures::stream::Peekable;
use gst::error_msg;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst::EventView;
use once_cell::sync::Lazy;
use gstthreadshare::runtime::prelude::*;
use gstthreadshare::runtime::{Context, PadSink, Task};
use std::sync::Mutex;
use std::task::Poll;
use std::time::{Duration, Instant};
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"ts-standalone-test-sink",
gst::DebugColorFlags::empty(),
Some("Thread-sharing standalone test sink"),
)
});
const DEFAULT_CONTEXT: &str = "";
const DEFAULT_CONTEXT_WAIT: Duration = Duration::from_millis(20);
const DEFAULT_PUSH_PERIOD: Duration = Duration::from_millis(20);
const DEFAULT_MAX_BUFFERS: i32 = 50 * (100 - 25);
const LOG_PERIOD: Duration = Duration::from_secs(20);
#[derive(Debug, Clone)]
struct Settings {
context: String,
context_wait: Duration,
raise_log_level: bool,
logs_stats: bool,
push_period: Duration,
max_buffers: Option<u32>,
}
impl Default for Settings {
fn default() -> Self {
Settings {
context: DEFAULT_CONTEXT.into(),
context_wait: DEFAULT_CONTEXT_WAIT,
raise_log_level: false,
logs_stats: false,
push_period: DEFAULT_PUSH_PERIOD,
max_buffers: Some(DEFAULT_MAX_BUFFERS as u32),
}
}
}
#[derive(Debug)]
enum StreamItem {
Buffer(gst::Buffer),
Event(gst::Event),
}
#[derive(Clone, Debug)]
struct TestSinkPadHandler;
impl PadSinkHandler for TestSinkPadHandler {
type ElementImpl = TestSink;
fn sink_chain(
self,
_pad: gst::Pad,
elem: super::TestSink,
buffer: gst::Buffer,
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
let sender = elem.imp().clone_item_sender();
async move {
if sender.send_async(StreamItem::Buffer(buffer)).await.is_err() {
gst::debug!(CAT, obj: elem, "Flushing");
return Err(gst::FlowError::Flushing);
}
Ok(gst::FlowSuccess::Ok)
}
.boxed()
}
fn sink_chain_list(
self,
_pad: gst::Pad,
elem: super::TestSink,
list: gst::BufferList,
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
let sender = elem.imp().clone_item_sender();
async move {
for buffer in list.iter_owned() {
if sender.send_async(StreamItem::Buffer(buffer)).await.is_err() {
gst::debug!(CAT, obj: elem, "Flushing");
return Err(gst::FlowError::Flushing);
}
}
Ok(gst::FlowSuccess::Ok)
}
.boxed()
}
fn sink_event_serialized(
self,
_pad: gst::Pad,
elem: super::TestSink,
event: gst::Event,
) -> BoxFuture<'static, bool> {
let sender = elem.imp().clone_item_sender();
async move {
if let EventView::FlushStop(_) = event.view() {
let imp = elem.imp();
return imp.task.flush_stop().await_maybe_on_context().is_ok();
} else if sender.send_async(StreamItem::Event(event)).await.is_err() {
gst::debug!(CAT, obj: elem, "Flushing");
}
true
}
.boxed()
}
fn sink_event(self, _pad: &gst::Pad, imp: &TestSink, event: gst::Event) -> bool {
if let EventView::FlushStart(..) = event.view() {
return imp.task.flush_start().await_maybe_on_context().is_ok();
}
true
}
}
#[derive(Default)]
struct Stats {
must_log: bool,
ramp_up_instant: Option<Instant>,
log_start_instant: Option<Instant>,
last_delta_instant: Option<Instant>,
max_buffers: Option<f32>,
buffer_count: f32,
buffer_count_delta: f32,
latency_sum: f32,
latency_square_sum: f32,
latency_sum_delta: f32,
latency_square_sum_delta: f32,
latency_min: Duration,
latency_min_delta: Duration,
latency_max: Duration,
latency_max_delta: Duration,
interval_sum: f32,
interval_square_sum: f32,
interval_sum_delta: f32,
interval_square_sum_delta: f32,
interval_min: Duration,
interval_min_delta: Duration,
interval_max: Duration,
interval_max_delta: Duration,
interval_late_warn: Duration,
interval_late_count: f32,
interval_late_count_delta: f32,
#[cfg(feature = "tuning")]
parked_duration_init: Duration,
}
impl Stats {
fn start(&mut self) {
if !self.must_log {
return;
}
self.buffer_count = 0.0;
self.buffer_count_delta = 0.0;
self.latency_sum = 0.0;
self.latency_square_sum = 0.0;
self.latency_sum_delta = 0.0;
self.latency_square_sum_delta = 0.0;
self.latency_min = Duration::MAX;
self.latency_min_delta = Duration::MAX;
self.latency_max = Duration::ZERO;
self.latency_max_delta = Duration::ZERO;
self.interval_sum = 0.0;
self.interval_square_sum = 0.0;
self.interval_sum_delta = 0.0;
self.interval_square_sum_delta = 0.0;
self.interval_min = Duration::MAX;
self.interval_min_delta = Duration::MAX;
self.interval_max = Duration::ZERO;
self.interval_max_delta = Duration::ZERO;
self.interval_late_count = 0.0;
self.interval_late_count_delta = 0.0;
self.last_delta_instant = None;
self.log_start_instant = None;
self.ramp_up_instant = Some(Instant::now());
gst::info!(CAT, "First stats logs in {:2?}", 2 * LOG_PERIOD);
}
fn is_active(&mut self) -> bool {
if !self.must_log {
return false;
}
if let Some(ramp_up_instant) = self.ramp_up_instant {
if ramp_up_instant.elapsed() < LOG_PERIOD {
return false;
}
self.ramp_up_instant = None;
gst::info!(CAT, "Ramp up complete. Stats logs in {:2?}", LOG_PERIOD);
self.log_start_instant = Some(Instant::now());
self.last_delta_instant = self.log_start_instant;
#[cfg(feature = "tuning")]
{
self.parked_duration_init = Context::current().unwrap().parked_duration();
}
}
use std::cmp::Ordering::*;
match self.max_buffers.opt_cmp(self.buffer_count) {
Some(Equal) => {
self.log_global();
self.buffer_count += 1.0;
false
}
Some(Less) => false,
_ => true,
}
}
fn add_buffer(&mut self, latency: Duration, interval: Duration) {
if !self.is_active() {
return;
}
self.buffer_count += 1.0;
self.buffer_count_delta += 1.0;
// Latency
let latency_f32 = latency.as_nanos() as f32;
let latency_square = latency_f32.powi(2);
self.latency_sum += latency_f32;
self.latency_square_sum += latency_square;
self.latency_min = self.latency_min.min(latency);
self.latency_max = self.latency_max.max(latency);
self.latency_sum_delta += latency_f32;
self.latency_square_sum_delta += latency_square;
self.latency_min_delta = self.latency_min_delta.min(latency);
self.latency_max_delta = self.latency_max_delta.max(latency);
// Interval
let interval_f32 = interval.as_nanos() as f32;
let interval_square = interval_f32.powi(2);
self.interval_sum += interval_f32;
self.interval_square_sum += interval_square;
self.interval_min = self.interval_min.min(interval);
self.interval_max = self.interval_max.max(interval);
self.interval_sum_delta += interval_f32;
self.interval_square_sum_delta += interval_square;
self.interval_min_delta = self.interval_min_delta.min(interval);
self.interval_max_delta = self.interval_max_delta.max(interval);
if interval > self.interval_late_warn {
self.interval_late_count += 1.0;
self.interval_late_count_delta += 1.0;
}
let delta_duration = match self.last_delta_instant {
Some(last_delta) => last_delta.elapsed(),
None => return,
};
if delta_duration < LOG_PERIOD {
return;
}
self.last_delta_instant = Some(Instant::now());
gst::info!(CAT, "Delta stats:");
let interval_mean = self.interval_sum_delta / self.buffer_count_delta;
let interval_std_dev = f32::sqrt(
self.interval_square_sum_delta / self.buffer_count_delta - interval_mean.powi(2),
);
gst::info!(
CAT,
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(interval_mean as u64),
Duration::from_nanos(interval_std_dev as u64),
self.interval_min_delta,
self.interval_max_delta,
);
if self.interval_late_count_delta > f32::EPSILON {
gst::warning!(
CAT,
"o {:5.2}% late buffers",
100f32 * self.interval_late_count_delta / self.buffer_count_delta
);
}
self.interval_sum_delta = 0.0;
self.interval_square_sum_delta = 0.0;
self.interval_min_delta = Duration::MAX;
self.interval_max_delta = Duration::ZERO;
self.interval_late_count_delta = 0.0;
let latency_mean = self.latency_sum_delta / self.buffer_count_delta;
let latency_std_dev = f32::sqrt(
self.latency_square_sum_delta / self.buffer_count_delta - latency_mean.powi(2),
);
gst::info!(
CAT,
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(latency_mean as u64),
Duration::from_nanos(latency_std_dev as u64),
self.latency_min_delta,
self.latency_max_delta,
);
self.latency_sum_delta = 0.0;
self.latency_square_sum_delta = 0.0;
self.latency_min_delta = Duration::MAX;
self.latency_max_delta = Duration::ZERO;
self.buffer_count_delta = 0.0;
}
fn log_global(&mut self) {
if self.buffer_count < 1.0 {
return;
}
let _log_start = if let Some(log_start) = self.log_start_instant {
log_start
} else {
return;
};
gst::info!(CAT, "Global stats:");
#[cfg(feature = "tuning")]
{
let duration = _log_start.elapsed();
let parked_duration =
Context::current().unwrap().parked_duration() - self.parked_duration_init;
gst::info!(
CAT,
"o parked: {parked_duration:4.2?} ({:5.2?}%)",
(parked_duration.as_nanos() as f32 * 100.0 / duration.as_nanos() as f32)
);
}
let interval_mean = self.interval_sum / self.buffer_count;
let interval_std_dev =
f32::sqrt(self.interval_square_sum / self.buffer_count - interval_mean.powi(2));
gst::info!(
CAT,
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(interval_mean as u64),
Duration::from_nanos(interval_std_dev as u64),
self.interval_min,
self.interval_max,
);
if self.interval_late_count > f32::EPSILON {
gst::warning!(
CAT,
"o {:5.2}% late buffers",
100f32 * self.interval_late_count / self.buffer_count
);
}
let latency_mean = self.latency_sum / self.buffer_count;
let latency_std_dev =
f32::sqrt(self.latency_square_sum / self.buffer_count - latency_mean.powi(2));
gst::info!(
CAT,
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(latency_mean as u64),
Duration::from_nanos(latency_std_dev as u64),
self.latency_min,
self.latency_max,
);
}
}
struct TestSinkTask {
element: super::TestSink,
raise_log_level: bool,
last_dts: Option<gst::ClockTime>,
item_receiver: Peekable<flume::r#async::RecvStream<'static, StreamItem>>,
stats: Stats,
segment: Option<gst::Segment>,
}
impl TestSinkTask {
fn new(element: &super::TestSink, item_receiver: flume::Receiver<StreamItem>) -> Self {
TestSinkTask {
element: element.clone(),
raise_log_level: false,
last_dts: None,
item_receiver: item_receiver.into_stream().peekable(),
stats: Stats::default(),
segment: None,
}
}
async fn flush(&mut self) {
// Purge the channel
while let Poll::Ready(Some(_item)) = futures::poll!(self.item_receiver.next()) {}
}
}
impl TaskImpl for TestSinkTask {
type Item = StreamItem;
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async move {
let sink = self.element.imp();
let settings = sink.settings.lock().unwrap();
self.raise_log_level = settings.raise_log_level;
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Preparing Task");
} else {
gst::trace!(CAT, obj: self.element, "Preparing Task");
}
self.stats.must_log = settings.logs_stats;
self.stats.max_buffers = settings.max_buffers.map(|max_buffers| max_buffers as f32);
self.stats.interval_late_warn = settings.push_period + settings.context_wait / 2;
Ok(())
}
.boxed()
}
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async {
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Starting Task");
} else {
gst::trace!(CAT, obj: self.element, "Starting Task");
}
self.last_dts = None;
self.stats.start();
Ok(())
}
.boxed()
}
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async {
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Stopping Task");
} else {
gst::trace!(CAT, obj: self.element, "Stopping Task");
}
self.flush().await;
Ok(())
}
.boxed()
}
fn try_next(&mut self) -> BoxFuture<'_, Result<StreamItem, gst::FlowError>> {
async move {
let item = self.item_receiver.next().await.unwrap();
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Popped item");
} else {
gst::trace!(CAT, obj: self.element, "Popped item");
}
Ok(item)
}
.boxed()
}
fn handle_item(&mut self, item: StreamItem) -> BoxFuture<'_, Result<(), gst::FlowError>> {
async move {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "Received {:?}", item);
} else {
gst::trace!(CAT, obj: self.element, "Received {:?}", item);
}
match item {
StreamItem::Buffer(buffer) => {
let dts = self
.segment
.as_ref()
.and_then(|segment| {
segment
.downcast_ref::<gst::format::Time>()
.and_then(|segment| segment.to_running_time(buffer.dts()))
})
.unwrap();
if let Some(last_dts) = self.last_dts {
let cur_ts = self.element.current_running_time().unwrap();
let latency: Duration = (cur_ts - dts).into();
let interval: Duration = (dts - last_dts).into();
self.stats.add_buffer(latency, interval);
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "o latency {:.2?}", latency);
gst::debug!(CAT, obj: self.element, "o interval {:.2?}", interval);
} else {
gst::trace!(CAT, obj: self.element, "o latency {:.2?}", latency);
gst::trace!(CAT, obj: self.element, "o interval {:.2?}", interval);
}
}
self.last_dts = Some(dts);
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Buffer processed");
} else {
gst::trace!(CAT, obj: self.element, "Buffer processed");
}
}
StreamItem::Event(event) => match event.view() {
EventView::Eos(_) => {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "EOS");
} else {
gst::trace!(CAT, obj: self.element, "EOS");
}
let elem = self.element.clone();
self.element.call_async(move |_| {
let _ =
elem.post_message(gst::message::Eos::builder().src(&elem).build());
});
return Err(gst::FlowError::Eos);
}
EventView::Segment(e) => {
self.segment = Some(e.segment().clone());
}
EventView::SinkMessage(e) => {
let _ = self.element.post_message(e.message());
}
_ => (),
},
}
Ok(())
}
.boxed()
}
}
#[derive(Debug)]
pub struct TestSink {
sink_pad: PadSink,
task: Task,
item_sender: Mutex<Option<flume::Sender<StreamItem>>>,
settings: Mutex<Settings>,
}
impl TestSink {
#[track_caller]
fn clone_item_sender(&self) -> flume::Sender<StreamItem> {
self.item_sender.lock().unwrap().as_ref().unwrap().clone()
}
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Preparing");
} else {
gst::trace!(CAT, imp: self, "Preparing");
}
let context = {
let settings = self.settings.lock().unwrap();
Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
error_msg!(
gst::ResourceError::OpenWrite,
["Failed to acquire Context: {}", err]
)
})?
};
// Enable backpressure for items
let (item_sender, item_receiver) = flume::bounded(0);
let task_impl = TestSinkTask::new(&*self.obj(), item_receiver);
self.task.prepare(task_impl, context).block_on()?;
*self.item_sender.lock().unwrap() = Some(item_sender);
if raise_log_level {
gst::debug!(CAT, imp: self, "Prepared");
} else {
gst::trace!(CAT, imp: self, "Prepared");
}
Ok(())
}
fn unprepare(&self) {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Unpreparing");
} else {
gst::trace!(CAT, imp: self, "Unpreparing");
}
self.task.unprepare().block_on().unwrap();
if raise_log_level {
gst::debug!(CAT, imp: self, "Unprepared");
} else {
gst::trace!(CAT, imp: self, "Unprepared");
}
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Stopping");
} else {
gst::trace!(CAT, imp: self, "Stopping");
}
self.task.stop().block_on()?;
if raise_log_level {
gst::debug!(CAT, imp: self, "Stopped");
} else {
gst::trace!(CAT, imp: self, "Stopped");
}
Ok(())
}
fn start(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Starting");
} else {
gst::trace!(CAT, imp: self, "Starting");
}
self.task.start().block_on()?;
if raise_log_level {
gst::debug!(CAT, imp: self, "Started");
} else {
gst::trace!(CAT, imp: self, "Started");
}
Ok(())
}
}
#[glib::object_subclass]
impl ObjectSubclass for TestSink {
const NAME: &'static str = "StandaloneTestSink";
type Type = super::TestSink;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
Self {
sink_pad: PadSink::new(
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
TestSinkPadHandler,
),
task: Task::default(),
item_sender: Default::default(),
settings: Default::default(),
}
}
}
impl ObjectImpl for TestSink {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecString::builder("context")
.nick("Context")
.blurb("Context name to share threads with")
.default_value(Some(DEFAULT_CONTEXT))
.build(),
glib::ParamSpecUInt::builder("context-wait")
.nick("Context Wait")
.blurb("Throttle poll loop to run at most once every this many ms")
.maximum(1000)
.default_value(DEFAULT_CONTEXT_WAIT.as_millis() as u32)
.build(),
glib::ParamSpecBoolean::builder("raise-log-level")
.nick("Raise log level")
.blurb("Raises the log level so that this element stands out")
.write_only()
.build(),
glib::ParamSpecBoolean::builder("logs-stats")
.nick("Logs Stats")
.blurb("Whether statistics should be logged")
.write_only()
.build(),
glib::ParamSpecUInt::builder("push-period")
.nick("Src buffer Push Period")
.blurb("Push period used by `src` element (used for stats warnings)")
.default_value(DEFAULT_PUSH_PERIOD.as_millis() as u32)
.build(),
glib::ParamSpecInt::builder("max-buffers")
.nick("Max Buffers")
.blurb("Number of buffers to count before stopping stats (-1 = unlimited)")
.minimum(-1i32)
.default_value(DEFAULT_MAX_BUFFERS)
.build(),
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
let mut settings = self.settings.lock().unwrap();
match pspec.name() {
"context" => {
settings.context = value
.get::<Option<String>>()
.expect("type checked upstream")
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
}
"context-wait" => {
settings.context_wait = Duration::from_millis(
value.get::<u32>().expect("type checked upstream").into(),
);
}
"raise-log-level" => {
settings.raise_log_level = value.get::<bool>().expect("type checked upstream");
}
"logs-stats" => {
let logs_stats = value.get().expect("type checked upstream");
settings.logs_stats = logs_stats;
}
"push-period" => {
settings.push_period = Duration::from_millis(
value.get::<u32>().expect("type checked upstream").into(),
);
}
"max-buffers" => {
let value = value.get::<i32>().expect("type checked upstream");
settings.max_buffers = if value > 0 { Some(value as u32) } else { None };
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let settings = self.settings.lock().unwrap();
match pspec.name() {
"context" => settings.context.to_value(),
"context-wait" => (settings.context_wait.as_millis() as u32).to_value(),
"raise-log-level" => settings.raise_log_level.to_value(),
"push-period" => (settings.push_period.as_millis() as u32).to_value(),
"max-buffers" => settings
.max_buffers
.and_then(|val| val.try_into().ok())
.unwrap_or(-1i32)
.to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
obj.set_element_flags(gst::ElementFlags::SINK);
}
}
impl GstObjectImpl for TestSink {}
impl ElementImpl for TestSink {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Thread-sharing standalone test sink",
"Sink/Test",
"Thread-sharing standalone test sink",
"François Laignel <fengalin@free.fr>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {:?}", transition);
match transition {
gst::StateChange::NullToReady => {
self.prepare().map_err(|err| {
self.post_error_message(err);
gst::StateChangeError
})?;
}
gst::StateChange::ReadyToPaused => {
self.start().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::PausedToReady => {
self.stop().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::ReadyToNull => {
self.unprepare();
}
_ => (),
}
self.parent_change_state(transition)
}
}

View file

@ -1,17 +1,22 @@
use gst::glib;
use gst::prelude::*;
pub mod async_mutex;
pub mod sync_mutex;
pub mod task;
mod imp;
mod settings;
pub use settings::Settings;
glib::wrapper! {
pub struct TestSink(ObjectSubclass<imp::TestSink>) @extends gst::Element, gst::Object;
}
mod stats;
pub use stats::Stats;
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"ts-standalone-test-sink",
gst::Rank::None,
TestSink::static_type(),
pub const ASYNC_MUTEX_ELEMENT_NAME: &str = "ts-standalone-async-mutex-sink";
pub const SYNC_MUTEX_ELEMENT_NAME: &str = "ts-standalone-sync-mutex-sink";
pub const TASK_ELEMENT_NAME: &str = "ts-standalone-task-sink";
use once_cell::sync::Lazy;
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"ts-standalone-sink",
gst::DebugColorFlags::empty(),
Some("Thread-sharing standalone test sink"),
)
}
});

View file

@ -0,0 +1,115 @@
use gst::glib;
use gst::prelude::*;
use std::time::Duration;
const DEFAULT_CONTEXT: &str = "";
const DEFAULT_CONTEXT_WAIT: Duration = Duration::from_millis(20);
const DEFAULT_PUSH_PERIOD: Duration = Duration::from_millis(20);
const DEFAULT_MAX_BUFFERS: i32 = 50 * (100 - 25);
#[derive(Debug, Clone)]
pub struct Settings {
pub context: String,
pub context_wait: Duration,
pub is_main_elem: bool,
pub logs_stats: bool,
pub push_period: Duration,
pub max_buffers: Option<u32>,
}
impl Default for Settings {
fn default() -> Self {
Settings {
context: DEFAULT_CONTEXT.into(),
context_wait: DEFAULT_CONTEXT_WAIT,
is_main_elem: false,
logs_stats: false,
push_period: DEFAULT_PUSH_PERIOD,
max_buffers: Some(DEFAULT_MAX_BUFFERS as u32),
}
}
}
impl Settings {
pub fn properties() -> Vec<glib::ParamSpec> {
vec![
glib::ParamSpecString::builder("context")
.nick("Context")
.blurb("Context name to share threads with")
.default_value(Some(DEFAULT_CONTEXT))
.build(),
glib::ParamSpecUInt::builder("context-wait")
.nick("Context Wait")
.blurb("Throttle poll loop to run at most once every this many ms")
.maximum(1000)
.default_value(DEFAULT_CONTEXT_WAIT.as_millis() as u32)
.build(),
glib::ParamSpecBoolean::builder("main-elem")
.nick("Main Element")
.blurb("Declare this element as the main one")
.write_only()
.build(),
glib::ParamSpecBoolean::builder("logs-stats")
.nick("Logs Stats")
.blurb("Whether statistics should be logged")
.write_only()
.build(),
glib::ParamSpecUInt::builder("push-period")
.nick("Src buffer Push Period")
.blurb("Push period used by `src` element (used for stats warnings)")
.default_value(DEFAULT_PUSH_PERIOD.as_millis() as u32)
.build(),
glib::ParamSpecInt::builder("max-buffers")
.nick("Max Buffers")
.blurb("Number of buffers to count before stopping stats (-1 = unlimited)")
.minimum(-1i32)
.default_value(DEFAULT_MAX_BUFFERS)
.build(),
]
}
pub fn set_property(&mut self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"context" => {
self.context = value
.get::<Option<String>>()
.unwrap()
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
}
"context-wait" => {
self.context_wait = Duration::from_millis(value.get::<u32>().unwrap().into());
}
"main-elem" => {
self.is_main_elem = value.get::<bool>().unwrap();
}
"logs-stats" => {
let logs_stats = value.get().unwrap();
self.logs_stats = logs_stats;
}
"push-period" => {
self.push_period = Duration::from_millis(value.get::<u32>().unwrap().into());
}
"max-buffers" => {
let value = value.get::<i32>().unwrap();
self.max_buffers = if value > 0 { Some(value as u32) } else { None };
}
_ => unimplemented!(),
}
}
pub fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"context" => self.context.to_value(),
"context-wait" => (self.context_wait.as_millis() as u32).to_value(),
"main-elem" => self.is_main_elem.to_value(),
"push-period" => (self.push_period.as_millis() as u32).to_value(),
"max-buffers" => self
.max_buffers
.and_then(|val| val.try_into().ok())
.unwrap_or(-1i32)
.to_value(),
_ => unimplemented!(),
}
}
}

View file

@ -0,0 +1,270 @@
use gst::prelude::*;
use std::time::{Duration, Instant};
#[cfg(feature = "tuning")]
use gstthreadshare::runtime::Context;
use super::CAT;
const LOG_PERIOD: Duration = Duration::from_secs(20);
#[derive(Debug, Default)]
pub struct Stats {
ramp_up_instant: Option<Instant>,
log_start_instant: Option<Instant>,
last_delta_instant: Option<Instant>,
max_buffers: Option<f32>,
buffer_count: f32,
buffer_count_delta: f32,
latency_sum: f32,
latency_square_sum: f32,
latency_sum_delta: f32,
latency_square_sum_delta: f32,
latency_min: Duration,
latency_min_delta: Duration,
latency_max: Duration,
latency_max_delta: Duration,
interval_sum: f32,
interval_square_sum: f32,
interval_sum_delta: f32,
interval_square_sum_delta: f32,
interval_min: Duration,
interval_min_delta: Duration,
interval_max: Duration,
interval_max_delta: Duration,
interval_late_warn: Duration,
interval_late_count: f32,
interval_late_count_delta: f32,
#[cfg(feature = "tuning")]
parked_duration_init: Duration,
}
impl Stats {
pub fn new(max_buffers: Option<u32>, interval_late_warn: Duration) -> Self {
Stats {
max_buffers: max_buffers.map(|max_buffers| max_buffers as f32),
interval_late_warn,
..Default::default()
}
}
pub fn start(&mut self) {
self.buffer_count = 0.0;
self.buffer_count_delta = 0.0;
self.latency_sum = 0.0;
self.latency_square_sum = 0.0;
self.latency_sum_delta = 0.0;
self.latency_square_sum_delta = 0.0;
self.latency_min = Duration::MAX;
self.latency_min_delta = Duration::MAX;
self.latency_max = Duration::ZERO;
self.latency_max_delta = Duration::ZERO;
self.interval_sum = 0.0;
self.interval_square_sum = 0.0;
self.interval_sum_delta = 0.0;
self.interval_square_sum_delta = 0.0;
self.interval_min = Duration::MAX;
self.interval_min_delta = Duration::MAX;
self.interval_max = Duration::ZERO;
self.interval_max_delta = Duration::ZERO;
self.interval_late_count = 0.0;
self.interval_late_count_delta = 0.0;
self.last_delta_instant = None;
self.log_start_instant = None;
self.ramp_up_instant = Some(Instant::now());
gst::info!(CAT, "First stats logs in {:2?}", 2 * LOG_PERIOD);
}
pub fn is_active(&mut self) -> bool {
if let Some(ramp_up_instant) = self.ramp_up_instant {
if ramp_up_instant.elapsed() < LOG_PERIOD {
return false;
}
self.ramp_up_instant = None;
gst::info!(CAT, "Ramp up complete. Stats logs in {:2?}", LOG_PERIOD);
self.log_start_instant = Some(Instant::now());
self.last_delta_instant = self.log_start_instant;
#[cfg(feature = "tuning")]
{
self.parked_duration_init = Context::current().unwrap().parked_duration();
}
}
use std::cmp::Ordering::*;
match self.max_buffers.opt_cmp(self.buffer_count) {
Some(Equal) => {
self.log_global();
self.buffer_count += 1.0;
false
}
Some(Less) => false,
_ => true,
}
}
pub fn add_buffer(&mut self, latency: Duration, interval: Duration) {
if !self.is_active() {
return;
}
self.buffer_count += 1.0;
self.buffer_count_delta += 1.0;
// Latency
let latency_f32 = latency.as_nanos() as f32;
let latency_square = latency_f32.powi(2);
self.latency_sum += latency_f32;
self.latency_square_sum += latency_square;
self.latency_min = self.latency_min.min(latency);
self.latency_max = self.latency_max.max(latency);
self.latency_sum_delta += latency_f32;
self.latency_square_sum_delta += latency_square;
self.latency_min_delta = self.latency_min_delta.min(latency);
self.latency_max_delta = self.latency_max_delta.max(latency);
// Interval
let interval_f32 = interval.as_nanos() as f32;
let interval_square = interval_f32.powi(2);
self.interval_sum += interval_f32;
self.interval_square_sum += interval_square;
self.interval_min = self.interval_min.min(interval);
self.interval_max = self.interval_max.max(interval);
self.interval_sum_delta += interval_f32;
self.interval_square_sum_delta += interval_square;
self.interval_min_delta = self.interval_min_delta.min(interval);
self.interval_max_delta = self.interval_max_delta.max(interval);
if interval > self.interval_late_warn {
self.interval_late_count += 1.0;
self.interval_late_count_delta += 1.0;
}
let delta_duration = match self.last_delta_instant {
Some(last_delta) => last_delta.elapsed(),
None => return,
};
if delta_duration < LOG_PERIOD {
return;
}
self.last_delta_instant = Some(Instant::now());
gst::info!(CAT, "Delta stats:");
let interval_mean = self.interval_sum_delta / self.buffer_count_delta;
let interval_std_dev = f32::sqrt(
self.interval_square_sum_delta / self.buffer_count_delta - interval_mean.powi(2),
);
gst::info!(
CAT,
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(interval_mean as u64),
Duration::from_nanos(interval_std_dev as u64),
self.interval_min_delta,
self.interval_max_delta,
);
if self.interval_late_count_delta > f32::EPSILON {
gst::warning!(
CAT,
"o {:5.2}% late buffers",
100f32 * self.interval_late_count_delta / self.buffer_count_delta
);
}
self.interval_sum_delta = 0.0;
self.interval_square_sum_delta = 0.0;
self.interval_min_delta = Duration::MAX;
self.interval_max_delta = Duration::ZERO;
self.interval_late_count_delta = 0.0;
let latency_mean = self.latency_sum_delta / self.buffer_count_delta;
let latency_std_dev = f32::sqrt(
self.latency_square_sum_delta / self.buffer_count_delta - latency_mean.powi(2),
);
gst::info!(
CAT,
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(latency_mean as u64),
Duration::from_nanos(latency_std_dev as u64),
self.latency_min_delta,
self.latency_max_delta,
);
self.latency_sum_delta = 0.0;
self.latency_square_sum_delta = 0.0;
self.latency_min_delta = Duration::MAX;
self.latency_max_delta = Duration::ZERO;
self.buffer_count_delta = 0.0;
}
pub fn log_global(&mut self) {
if self.buffer_count < 1.0 {
return;
}
let _log_start = if let Some(log_start) = self.log_start_instant {
log_start
} else {
return;
};
gst::info!(CAT, "Global stats:");
#[cfg(feature = "tuning")]
{
let duration = _log_start.elapsed();
let parked_duration =
Context::current().unwrap().parked_duration() - self.parked_duration_init;
gst::info!(
CAT,
"o parked: {parked_duration:4.2?} ({:5.2?}%)",
(parked_duration.as_nanos() as f32 * 100.0 / duration.as_nanos() as f32)
);
}
let interval_mean = self.interval_sum / self.buffer_count;
let interval_std_dev =
f32::sqrt(self.interval_square_sum / self.buffer_count - interval_mean.powi(2));
gst::info!(
CAT,
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(interval_mean as u64),
Duration::from_nanos(interval_std_dev as u64),
self.interval_min,
self.interval_max,
);
if self.interval_late_count > f32::EPSILON {
gst::warning!(
CAT,
"o {:5.2}% late buffers",
100f32 * self.interval_late_count / self.buffer_count
);
}
let latency_mean = self.latency_sum / self.buffer_count;
let latency_std_dev =
f32::sqrt(self.latency_square_sum / self.buffer_count - latency_mean.powi(2));
gst::info!(
CAT,
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
Duration::from_nanos(latency_mean as u64),
Duration::from_nanos(latency_std_dev as u64),
self.latency_min,
self.latency_max,
);
}
}

View file

@ -0,0 +1,327 @@
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use futures::future::BoxFuture;
use futures::prelude::*;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst::EventView;
use once_cell::sync::Lazy;
use gstthreadshare::runtime::{prelude::*, PadSink};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use super::super::{Settings, Stats, CAT};
#[derive(Debug, Default)]
struct PadSinkHandlerInner {
is_flushing: bool,
is_main_elem: bool,
last_dts: Option<gst::ClockTime>,
segment_start: Option<gst::ClockTime>,
stats: Option<Box<Stats>>,
}
impl PadSinkHandlerInner {
fn handle_buffer(
&mut self,
elem: &super::DirectSink,
buffer: gst::Buffer,
) -> Result<(), gst::FlowError> {
if self.is_flushing {
log_or_trace!(
CAT,
self.is_main_elem,
obj: elem,
"Discarding {buffer:?} (flushing)"
);
return Err(gst::FlowError::Flushing);
}
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "Received {buffer:?}");
let dts = buffer
.dts()
.expect("Buffer without dts")
.checked_sub(self.segment_start.expect("Buffer without Time Segment"))
.expect("dts before Segment start");
if let Some(last_dts) = self.last_dts {
let cur_ts = elem.current_running_time().unwrap();
let latency: Duration = (cur_ts - dts).into();
let interval: Duration = (dts - last_dts).into();
if let Some(stats) = self.stats.as_mut() {
stats.add_buffer(latency, interval);
}
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "o latency {latency:.2?}");
debug_or_trace!(
CAT,
self.is_main_elem,
obj: elem,
"o interval {interval:.2?}",
);
}
self.last_dts = Some(dts);
log_or_trace!(CAT, self.is_main_elem, obj: elem, "Buffer processed");
Ok(())
}
}
#[derive(Clone, Debug, Default)]
struct SyncPadSinkHandler(Arc<Mutex<PadSinkHandlerInner>>);
impl PadSinkHandler for SyncPadSinkHandler {
type ElementImpl = DirectSink;
fn sink_chain(
self,
_pad: gst::Pad,
elem: super::DirectSink,
buffer: gst::Buffer,
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
async move {
if self.0.lock().unwrap().handle_buffer(&elem, buffer).is_err() {
return Err(gst::FlowError::Flushing);
}
Ok(gst::FlowSuccess::Ok)
}
.boxed()
}
fn sink_event_serialized(
self,
_pad: gst::Pad,
elem: super::DirectSink,
event: gst::Event,
) -> BoxFuture<'static, bool> {
async move {
match event.view() {
EventView::Eos(_) => {
{
let mut inner = self.0.lock().unwrap();
debug_or_trace!(CAT, inner.is_main_elem, obj: elem, "EOS");
inner.is_flushing = true;
}
// When each element sends its own EOS message,
// it takes ages for the pipeline to process all of them.
// Let's just post an error message and let main shuts down
// after all streams have posted this message.
let _ = elem
.post_message(gst::message::Error::new(gst::LibraryError::Shutdown, "EOS"));
}
EventView::FlushStop(_) => {
self.0.lock().unwrap().is_flushing = false;
}
EventView::Segment(evt) => {
if let Some(time_seg) = evt.segment().downcast_ref::<gst::ClockTime>() {
self.0.lock().unwrap().segment_start = time_seg.start();
}
}
EventView::SinkMessage(evt) => {
let _ = elem.post_message(evt.message());
}
_ => (),
}
true
}
.boxed()
}
fn sink_event(self, _pad: &gst::Pad, _imp: &DirectSink, event: gst::Event) -> bool {
if let EventView::FlushStart(..) = event.view() {
self.0.lock().unwrap().is_flushing = true;
}
true
}
}
impl SyncPadSinkHandler {
fn prepare(&self, is_main_elem: bool, stats: Option<Stats>) {
let mut inner = self.0.lock().unwrap();
inner.is_main_elem = is_main_elem;
inner.stats = stats.map(Box::new);
}
fn start(&self) {
let mut inner = self.0.lock().unwrap();
inner.is_flushing = false;
inner.last_dts = None;
if let Some(stats) = inner.stats.as_mut() {
stats.start();
}
}
fn stop(&self) {
let mut inner = self.0.lock().unwrap();
inner.is_flushing = true;
}
}
#[derive(Debug)]
pub struct DirectSink {
sink_pad: PadSink,
sink_pad_handler: SyncPadSinkHandler,
settings: Mutex<Settings>,
}
impl DirectSink {
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
let settings = self.settings.lock().unwrap();
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Preparing");
let stats = if settings.logs_stats {
Some(Stats::new(
settings.max_buffers,
settings.push_period + settings.context_wait / 2,
))
} else {
None
};
self.sink_pad_handler.prepare(settings.is_main_elem, stats);
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Prepared");
Ok(())
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
self.sink_pad_handler.stop();
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
Ok(())
}
fn start(&self) -> Result<(), gst::ErrorMessage> {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
self.sink_pad_handler.start();
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
Ok(())
}
}
#[glib::object_subclass]
impl ObjectSubclass for DirectSink {
const NAME: &'static str = "TsStandaloneDirectSink";
type Type = super::DirectSink;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
let sink_pad_handler = SyncPadSinkHandler::default();
Self {
sink_pad: PadSink::new(
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
sink_pad_handler.clone(),
),
sink_pad_handler,
settings: Default::default(),
}
}
}
impl ObjectImpl for DirectSink {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(Settings::properties);
PROPERTIES.as_ref()
}
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
self.settings.lock().unwrap().set_property(id, value, pspec);
}
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.settings.lock().unwrap().property(id, pspec)
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
obj.set_element_flags(gst::ElementFlags::SINK);
}
}
impl GstObjectImpl for DirectSink {}
impl ElementImpl for DirectSink {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Thread-sharing standalone test direct sink",
"Sink/Test",
"Thread-sharing standalone test direct sink",
"François Laignel <fengalin@free.fr>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {transition:?}");
match transition {
gst::StateChange::NullToReady => {
self.prepare().map_err(|err| {
self.post_error_message(err);
gst::StateChangeError
})?;
}
gst::StateChange::ReadyToPaused => {
self.start().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::PausedToReady => {
self.stop().map_err(|_| gst::StateChangeError)?;
}
_ => (),
}
self.parent_change_state(transition)
}
}

View file

@ -0,0 +1,17 @@
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct DirectSink(ObjectSubclass<imp::DirectSink>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
super::SYNC_MUTEX_ELEMENT_NAME,
gst::Rank::None,
DirectSink::static_type(),
)
}

View file

@ -0,0 +1,402 @@
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use futures::future::BoxFuture;
use futures::prelude::*;
use gst::error_msg;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst::EventView;
use once_cell::sync::Lazy;
use gstthreadshare::runtime::prelude::*;
use gstthreadshare::runtime::{Context, PadSink, Task};
use std::sync::Mutex;
use std::time::Duration;
use super::super::{Settings, Stats, CAT};
#[derive(Debug)]
enum StreamItem {
Buffer(gst::Buffer),
Event(gst::Event),
}
#[derive(Clone, Debug)]
struct TaskPadSinkHandler;
impl PadSinkHandler for TaskPadSinkHandler {
type ElementImpl = TaskSink;
fn sink_chain(
self,
_pad: gst::Pad,
elem: super::TaskSink,
buffer: gst::Buffer,
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
let sender = elem.imp().clone_item_sender();
async move {
if sender.send_async(StreamItem::Buffer(buffer)).await.is_err() {
return Err(gst::FlowError::Flushing);
}
Ok(gst::FlowSuccess::Ok)
}
.boxed()
}
fn sink_event_serialized(
self,
_pad: gst::Pad,
elem: super::TaskSink,
event: gst::Event,
) -> BoxFuture<'static, bool> {
let sender = elem.imp().clone_item_sender();
async move {
match event.view() {
EventView::Segment(_) => {
let _ = sender.send_async(StreamItem::Event(event)).await;
}
EventView::Eos(_) => {
let is_main_elem = elem.imp().settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, obj: elem, "EOS");
// When each element sends its own EOS message,
// it takes ages for the pipeline to process all of them.
// Let's just post an error message and let main shuts down
// after all streams have posted this message.
let _ = elem
.post_message(gst::message::Error::new(gst::LibraryError::Shutdown, "EOS"));
}
EventView::FlushStop(_) => {
let imp = elem.imp();
return imp.task.flush_stop().await_maybe_on_context().is_ok();
}
EventView::SinkMessage(evt) => {
let _ = elem.post_message(evt.message());
}
_ => (),
}
true
}
.boxed()
}
fn sink_event(self, _pad: &gst::Pad, imp: &TaskSink, event: gst::Event) -> bool {
if let EventView::FlushStart(..) = event.view() {
return imp.task.flush_start().await_maybe_on_context().is_ok();
}
true
}
}
struct TaskSinkTask {
elem: super::TaskSink,
item_receiver: flume::Receiver<StreamItem>,
is_main_elem: bool,
last_dts: Option<gst::ClockTime>,
segment_start: Option<gst::ClockTime>,
stats: Option<Box<Stats>>,
}
impl TaskSinkTask {
fn new(
elem: &super::TaskSink,
item_receiver: flume::Receiver<StreamItem>,
is_main_elem: bool,
stats: Option<Box<Stats>>,
) -> Self {
TaskSinkTask {
elem: elem.clone(),
item_receiver,
is_main_elem,
last_dts: None,
stats,
segment_start: None,
}
}
fn flush(&mut self) {
// Purge the channel
while !self.item_receiver.is_empty() {}
}
}
impl TaskImpl for TaskSinkTask {
type Item = StreamItem;
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Preparing Task");
future::ok(()).boxed()
}
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async {
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Starting Task");
self.last_dts = None;
if let Some(stats) = self.stats.as_mut() {
stats.start();
}
Ok(())
}
.boxed()
}
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async {
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Stopping Task");
self.flush();
Ok(())
}
.boxed()
}
fn try_next(&mut self) -> BoxFuture<'_, Result<StreamItem, gst::FlowError>> {
self.item_receiver
.recv_async()
.map(|opt_item| Ok(opt_item.unwrap()))
.boxed()
}
fn handle_item(&mut self, item: StreamItem) -> BoxFuture<'_, Result<(), gst::FlowError>> {
async move {
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Received {item:?}");
match item {
StreamItem::Buffer(buffer) => {
let dts = buffer
.dts()
.expect("Buffer without dts")
.checked_sub(self.segment_start.expect("Buffer without Time Segment"))
.expect("dts before Segment start");
if let Some(last_dts) = self.last_dts {
let cur_ts = self.elem.current_running_time().unwrap();
let latency: Duration = (cur_ts - dts).into();
let interval: Duration = (dts - last_dts).into();
if let Some(stats) = self.stats.as_mut() {
stats.add_buffer(latency, interval);
}
debug_or_trace!(
CAT,
self.is_main_elem,
obj: self.elem,
"o latency {latency:.2?}",
);
debug_or_trace!(
CAT,
self.is_main_elem,
obj: self.elem,
"o interval {interval:.2?}",
);
}
self.last_dts = Some(dts);
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Buffer processed");
}
StreamItem::Event(evt) => {
if let EventView::Segment(evt) = evt.view() {
if let Some(time_seg) = evt.segment().downcast_ref::<gst::ClockTime>() {
self.segment_start = time_seg.start();
}
}
}
}
Ok(())
}
.boxed()
}
}
#[derive(Debug)]
pub struct TaskSink {
sink_pad: PadSink,
task: Task,
item_sender: Mutex<Option<flume::Sender<StreamItem>>>,
settings: Mutex<Settings>,
}
impl TaskSink {
#[track_caller]
fn clone_item_sender(&self) -> flume::Sender<StreamItem> {
self.item_sender.lock().unwrap().as_ref().unwrap().clone()
}
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
let settings = self.settings.lock().unwrap();
let stats = if settings.logs_stats {
Some(Box::new(Stats::new(
settings.max_buffers,
settings.push_period + settings.context_wait / 2,
)))
} else {
None
};
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Preparing");
let ts_ctx = Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
error_msg!(
gst::ResourceError::OpenWrite,
["Failed to acquire Context: {}", err]
)
})?;
// Enable backpressure for items
let (item_sender, item_receiver) = flume::bounded(0);
let task_impl = TaskSinkTask::new(&self.obj(), item_receiver, settings.is_main_elem, stats);
self.task.prepare(task_impl, ts_ctx).block_on()?;
*self.item_sender.lock().unwrap() = Some(item_sender);
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Prepared");
Ok(())
}
fn unprepare(&self) {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Unpreparing");
self.task.unprepare().block_on().unwrap();
debug_or_trace!(CAT, is_main_elem, imp: self, "Unprepared");
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
self.task.stop().block_on()?;
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
Ok(())
}
fn start(&self) -> Result<(), gst::ErrorMessage> {
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
self.task.start().block_on()?;
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
Ok(())
}
}
#[glib::object_subclass]
impl ObjectSubclass for TaskSink {
const NAME: &'static str = "TsStandaloneTaskSink";
type Type = super::TaskSink;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
Self {
sink_pad: PadSink::new(
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
TaskPadSinkHandler,
),
task: Task::default(),
item_sender: Default::default(),
settings: Default::default(),
}
}
}
impl ObjectImpl for TaskSink {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(Settings::properties);
PROPERTIES.as_ref()
}
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
self.settings.lock().unwrap().set_property(id, value, pspec);
}
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
self.settings.lock().unwrap().property(id, pspec)
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
obj.set_element_flags(gst::ElementFlags::SINK);
}
}
impl GstObjectImpl for TaskSink {}
impl ElementImpl for TaskSink {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Thread-sharing standalone test task sink",
"Sink/Test",
"Thread-sharing standalone test task sink",
"François Laignel <fengalin@free.fr>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {transition:?}");
match transition {
gst::StateChange::NullToReady => {
self.prepare().map_err(|err| {
self.post_error_message(err);
gst::StateChangeError
})?;
}
gst::StateChange::ReadyToPaused => {
self.start().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::PausedToReady => {
self.stop().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::ReadyToNull => {
self.unprepare();
}
_ => (),
}
self.parent_change_state(transition)
}
}

View file

@ -0,0 +1,17 @@
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct TaskSink(ObjectSubclass<imp::TaskSink>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
super::TASK_ELEMENT_NAME,
gst::Rank::None,
TaskSink::static_type(),
)
}

View file

@ -19,11 +19,11 @@ use std::sync::Mutex;
use std::time::Duration;
use gstthreadshare::runtime::prelude::*;
use gstthreadshare::runtime::{timer, Context, PadSrc, Task};
use gstthreadshare::runtime::{task, timer, Context, PadSrc, Task};
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"ts-standalone-test-src",
super::ELEMENT_NAME,
gst::DebugColorFlags::empty(),
Some("Thread-sharing standalone test src"),
)
@ -39,7 +39,7 @@ struct Settings {
context: String,
context_wait: Duration,
push_period: gst::ClockTime,
raise_log_level: bool,
is_main_elem: bool,
num_buffers: Option<u32>,
}
@ -49,7 +49,7 @@ impl Default for Settings {
context: DEFAULT_CONTEXT.into(),
context_wait: DEFAULT_CONTEXT_WAIT,
push_period: DEFAULT_PUSH_PERIOD,
raise_log_level: false,
is_main_elem: false,
num_buffers: Some(DEFAULT_NUM_BUFFERS as u32),
}
}
@ -63,19 +63,18 @@ impl PadSrcHandler for TestSrcPadHandler {
#[derive(Debug)]
struct SrcTask {
element: super::TestSrc,
elem: super::TestSrc,
buffer_pool: gst::BufferPool,
timer: Option<timer::Interval>,
raise_log_level: bool,
is_main_elem: bool,
push_period: gst::ClockTime,
need_initial_events: bool,
need_segment: bool,
num_buffers: Option<u32>,
buffer_count: u32,
}
impl SrcTask {
fn new(element: super::TestSrc) -> Self {
fn new(elem: super::TestSrc) -> Self {
let buffer_pool = gst::BufferPool::new();
let mut pool_config = buffer_pool.config();
pool_config
@ -84,13 +83,12 @@ impl SrcTask {
buffer_pool.set_config(pool_config).unwrap();
SrcTask {
element,
elem,
buffer_pool,
timer: None,
raise_log_level: false,
is_main_elem: false,
push_period: gst::ClockTime::ZERO,
need_initial_events: true,
need_segment: true,
num_buffers: Some(DEFAULT_NUM_BUFFERS as u32),
buffer_count: 0,
}
@ -98,34 +96,48 @@ impl SrcTask {
}
impl TaskImpl for SrcTask {
type Item = gst::Buffer;
type Item = ();
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async move {
let src = self.element.imp();
let settings = src.settings.lock().unwrap();
self.raise_log_level = settings.raise_log_level;
let imp = self.elem.imp();
let settings = imp.settings.lock().unwrap();
self.is_main_elem = settings.is_main_elem;
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Preparing Task");
} else {
gst::trace!(CAT, obj: self.element, "Preparing Task");
}
log_or_trace!(CAT, self.is_main_elem, imp: imp, "Preparing Task");
self.push_period = settings.push_period;
self.num_buffers = settings.num_buffers;
self.push_period = settings.push_period;
self.num_buffers = settings.num_buffers;
Ok(())
}
.boxed()
future::ok(()).boxed()
}
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async {
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Starting Task");
} else {
gst::trace!(CAT, obj: self.element, "Starting Task");
async move {
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Starting Task");
if self.need_initial_events {
let imp = self.elem.imp();
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Pushing initial events");
let stream_id =
format!("{:08x}{:08x}", rand::random::<u32>(), rand::random::<u32>());
let stream_start_evt = gst::event::StreamStart::builder(&stream_id)
.group_id(gst::GroupId::next())
.build();
imp.src_pad.push_event(stream_start_evt).await;
imp.src_pad
.push_event(gst::event::Caps::new(
&gst::Caps::builder("foo/bar").build(),
))
.await;
let segment_evt =
gst::event::Segment::new(&gst::FormattedSegment::<gst::format::Time>::new());
imp.src_pad.push_event(segment_evt).await;
self.need_initial_events = false;
}
self.timer = Some(
@ -138,178 +150,100 @@ impl TaskImpl for SrcTask {
);
self.buffer_count = 0;
self.buffer_pool.set_active(true).unwrap();
Ok(())
}
.boxed()
}
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async move {
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Stopping Task");
} else {
gst::trace!(CAT, obj: self.element, "Stopping Task");
}
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Stopping Task");
self.buffer_pool.set_active(false).unwrap();
self.timer = None;
self.need_initial_events = true;
self.buffer_pool.set_active(false).unwrap();
self.timer = None;
self.need_initial_events = true;
self.need_segment = true;
future::ok(()).boxed()
}
fn try_next(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
async move {
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Awaiting timer");
self.timer.as_mut().unwrap().next().await;
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Timer ticked");
Ok(())
}
.boxed()
}
fn try_next(&mut self) -> BoxFuture<'_, Result<gst::Buffer, gst::FlowError>> {
fn handle_item(&mut self, _: ()) -> BoxFuture<'_, Result<(), gst::FlowError>> {
async move {
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Awaiting timer");
} else {
gst::trace!(CAT, obj: self.element, "Awaiting timer");
}
self.timer.as_mut().unwrap().next().await;
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Timer ticked");
} else {
gst::trace!(CAT, obj: self.element, "Timer ticked");
}
self.buffer_pool
let buffer = self
.buffer_pool
.acquire_buffer(None)
.map(|mut buffer| {
{
let buffer = buffer.get_mut().unwrap();
let rtime = self.element.current_running_time().unwrap();
let rtime = self.elem.current_running_time().unwrap();
buffer.set_dts(rtime);
}
buffer
})
.map_err(|err| {
gst::error!(CAT, obj: self.element, "Failed to acquire buffer {}", err);
gst::error!(CAT, obj: self.elem, "Failed to acquire buffer {err}");
err
})
})?;
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Forwarding buffer");
self.elem.imp().src_pad.push(buffer).await?;
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Successfully pushed buffer");
self.buffer_count += 1;
if self.num_buffers.opt_eq(self.buffer_count) == Some(true) {
return Err(gst::FlowError::Eos);
}
Ok(())
}
.boxed()
}
fn handle_item(&mut self, buffer: gst::Buffer) -> BoxFuture<'_, Result<(), gst::FlowError>> {
fn handle_loop_error(&mut self, err: gst::FlowError) -> BoxFuture<'_, task::Trigger> {
async move {
let res = self.push(buffer).await;
match res {
Ok(_) => {
if self.raise_log_level {
gst::log!(CAT, obj: self.element, "Successfully pushed buffer");
} else {
gst::trace!(CAT, obj: self.element, "Successfully pushed buffer");
}
}
Err(gst::FlowError::Eos) => {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "EOS");
} else {
gst::trace!(CAT, obj: self.element, "EOS");
}
let test_src = self.element.imp();
test_src.src_pad.push_event(gst::event::Eos::new()).await;
match err {
gst::FlowError::Eos => {
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Pushing EOS");
return Err(gst::FlowError::Eos);
}
Err(gst::FlowError::Flushing) => {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "Flushing");
} else {
gst::trace!(CAT, obj: self.element, "Flushing");
let imp = self.elem.imp();
if !imp.src_pad.push_event(gst::event::Eos::new()).await {
gst::error!(CAT, imp: imp, "Error pushing EOS");
}
task::Trigger::Stop
}
Err(err) => {
gst::error!(CAT, obj: self.element, "Got error {}", err);
gst::FlowError::Flushing => {
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Flushing");
task::Trigger::FlushStart
}
err => {
gst::error!(CAT, obj: self.elem, "Got error {err}");
gst::element_error!(
&self.element,
&self.elem,
gst::StreamError::Failed,
("Internal data stream error"),
["streaming stopped, reason {}", err]
);
task::Trigger::Error
}
}
res.map(drop)
}
.boxed()
}
}
impl SrcTask {
async fn push(&mut self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "Pushing {:?}", buffer);
} else {
gst::trace!(CAT, obj: self.element, "Pushing {:?}", buffer);
}
let test_src = self.element.imp();
if self.need_initial_events {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "Pushing initial events");
} else {
gst::trace!(CAT, obj: self.element, "Pushing initial events");
}
let stream_id = format!("{:08x}{:08x}", rand::random::<u32>(), rand::random::<u32>());
let stream_start_evt = gst::event::StreamStart::builder(&stream_id)
.group_id(gst::GroupId::next())
.build();
test_src.src_pad.push_event(stream_start_evt).await;
test_src
.src_pad
.push_event(gst::event::Caps::new(
&gst::Caps::builder("foo/bar").build(),
))
.await;
self.need_initial_events = false;
}
if self.need_segment {
let segment_evt =
gst::event::Segment::new(&gst::FormattedSegment::<gst::format::Time>::new());
test_src.src_pad.push_event(segment_evt).await;
self.need_segment = false;
}
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "Forwarding buffer");
} else {
gst::trace!(CAT, obj: self.element, "Forwarding buffer");
}
let ok = test_src.src_pad.push(buffer).await?;
self.buffer_count += 1;
if self.num_buffers.opt_eq(self.buffer_count).unwrap_or(false) {
if self.raise_log_level {
gst::debug!(CAT, obj: self.element, "Pushing EOS");
} else {
gst::trace!(CAT, obj: self.element, "Pushing EOS");
}
let test_src = self.element.imp();
if !test_src.src_pad.push_event(gst::event::Eos::new()).await {
gst::error!(CAT, obj: self.element, "Error pushing EOS");
}
return Err(gst::FlowError::Eos);
}
Ok(ok)
}
}
#[derive(Debug)]
pub struct TestSrc {
src_pad: PadSrc,
@ -319,106 +253,57 @@ pub struct TestSrc {
impl TestSrc {
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Preparing");
} else {
gst::trace!(CAT, imp: self, "Preparing");
}
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Preparing");
let settings = self.settings.lock().unwrap();
let context =
Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
gst::error_msg!(
gst::ResourceError::OpenRead,
["Failed to acquire Context: {}", err]
)
})?;
let ts_ctx = Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
gst::error_msg!(
gst::ResourceError::OpenRead,
["Failed to acquire Context: {}", err]
)
})?;
drop(settings);
self.task
.prepare(SrcTask::new(self.obj().clone()), context)
.prepare(SrcTask::new(self.instance().clone()), ts_ctx)
.block_on()?;
if raise_log_level {
gst::debug!(CAT, imp: self, "Prepared");
} else {
gst::trace!(CAT, imp: self, "Prepared");
}
debug_or_trace!(CAT, is_main_elem, imp: self, "Prepared");
Ok(())
}
fn unprepare(&self) {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Unpreparing");
} else {
gst::trace!(CAT, imp: self, "Unpreparing");
}
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Unpreparing");
self.task.unprepare().block_on().unwrap();
if raise_log_level {
gst::debug!(CAT, imp: self, "Unprepared");
} else {
gst::trace!(CAT, imp: self, "Unprepared");
}
debug_or_trace!(CAT, is_main_elem, imp: self, "Unprepared");
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Stopping");
} else {
gst::trace!(CAT, imp: self, "Stopping");
}
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
self.task.stop().block_on()?;
if raise_log_level {
gst::debug!(CAT, imp: self, "Stopped");
} else {
gst::trace!(CAT, imp: self, "Stopped");
}
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
Ok(())
}
fn start(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Starting");
} else {
gst::trace!(CAT, imp: self, "Starting");
}
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
self.task.start().block_on()?;
if raise_log_level {
gst::debug!(CAT, imp: self, "Started");
} else {
gst::trace!(CAT, imp: self, "Started");
}
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
Ok(())
}
fn pause(&self) -> Result<(), gst::ErrorMessage> {
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
if raise_log_level {
gst::debug!(CAT, imp: self, "Pausing");
} else {
gst::trace!(CAT, imp: self, "Pausing");
}
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
debug_or_trace!(CAT, is_main_elem, imp: self, "Pausing");
self.task.pause().block_on()?;
if raise_log_level {
gst::debug!(CAT, imp: self, "Paused");
} else {
gst::trace!(CAT, imp: self, "Paused");
}
debug_or_trace!(CAT, is_main_elem, imp: self, "Paused");
Ok(())
}
@ -462,9 +347,9 @@ impl ObjectImpl for TestSrc {
.blurb("Push a new buffer every this many ms")
.default_value(DEFAULT_PUSH_PERIOD.mseconds() as u32)
.build(),
glib::ParamSpecBoolean::builder("raise-log-level")
.nick("Raise log level")
.blurb("Raises the log level so that this element stands out")
glib::ParamSpecBoolean::builder("main-elem")
.nick("Main Element")
.blurb("Declare this element as the main one")
.write_only()
.build(),
glib::ParamSpecInt::builder("num-buffers")
@ -485,24 +370,21 @@ impl ObjectImpl for TestSrc {
"context" => {
settings.context = value
.get::<Option<String>>()
.expect("type checked upstream")
.unwrap()
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
}
"context-wait" => {
settings.context_wait = Duration::from_millis(
value.get::<u32>().expect("type checked upstream").into(),
);
settings.context_wait = Duration::from_millis(value.get::<u32>().unwrap().into());
}
"push-period" => {
settings.push_period = gst::ClockTime::from_mseconds(
value.get::<u32>().expect("type checked upstream").into(),
);
let value: u64 = value.get::<u32>().unwrap().into();
settings.push_period = value.mseconds();
}
"raise-log-level" => {
settings.raise_log_level = value.get::<bool>().expect("type checked upstream");
"main-elem" => {
settings.is_main_elem = value.get::<bool>().unwrap();
}
"num-buffers" => {
let value = value.get::<i32>().expect("type checked upstream");
let value = value.get::<i32>().unwrap();
settings.num_buffers = if value > 0 { Some(value as u32) } else { None };
}
_ => unimplemented!(),
@ -515,7 +397,7 @@ impl ObjectImpl for TestSrc {
"context" => settings.context.to_value(),
"context-wait" => (settings.context_wait.as_millis() as u32).to_value(),
"push-period" => (settings.push_period.mseconds() as u32).to_value(),
"raise-log-level" => settings.raise_log_level.to_value(),
"main-elem" => settings.is_main_elem.to_value(),
"num-buffers" => settings
.num_buffers
.and_then(|val| val.try_into().ok())
@ -571,7 +453,7 @@ impl ElementImpl for TestSrc {
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {:?}", transition);
gst::trace!(CAT, imp: self, "Changing state {transition:?}");
match transition {
gst::StateChange::NullToReady => {

View file

@ -3,6 +3,8 @@ use gst::prelude::*;
mod imp;
pub const ELEMENT_NAME: &str = "ts-standalone-src";
glib::wrapper! {
pub struct TestSrc(ObjectSubclass<imp::TestSrc>) @extends gst::Element, gst::Object;
}
@ -10,7 +12,7 @@ glib::wrapper! {
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"ts-standalone-test-src",
"ts-standalone-src",
gst::Rank::None,
TestSrc::static_type(),
)

View file

@ -17,19 +17,45 @@
//
// SPDX-License-Identifier: LGPL-2.1-or-later
use gst::glib;
use gst::prelude::*;
use once_cell::sync::Lazy;
use std::net;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::{env, thread, time};
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"ts-udpsrc-benchmark-sender",
gst::DebugColorFlags::empty(),
Some("Thread-sharing UDP src benchmark sender"),
)
});
fn main() {
gst::init().unwrap();
gstthreadshare::plugin_register_static().unwrap();
let args = env::args().collect::<Vec<_>>();
assert!(args.len() > 1);
let n_streams: u16 = args[1].parse().unwrap();
if args.len() > 2 && args[2] == "rtp" {
send_rtp_buffers(n_streams);
let num_buffers: Option<i32> = if args.len() > 3 {
args[3].parse().ok()
} else {
send_raw_buffers(n_streams);
None
};
if args.len() > 2 {
match args[2].as_str() {
"raw" => send_raw_buffers(n_streams),
"rtp" => send_rtp_buffers(n_streams, num_buffers),
_ => send_test_buffers(n_streams, num_buffers),
}
} else {
send_test_buffers(n_streams, num_buffers);
}
}
@ -38,7 +64,7 @@ fn send_raw_buffers(n_streams: u16) {
let socket = net::UdpSocket::bind("0.0.0.0:0").unwrap();
let ipaddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
let destinations = (40000..(40000 + n_streams))
let destinations = (5004..(5004 + n_streams))
.map(|port| SocketAddr::new(ipaddr, port))
.collect::<Vec<_>>();
@ -60,43 +86,60 @@ fn send_raw_buffers(n_streams: u16) {
}
}
fn send_rtp_buffers(n_streams: u16) {
use gst::glib;
use gst::prelude::*;
gst::init().unwrap();
#[cfg(debug_assertions)]
{
use std::path::Path;
let mut path = Path::new("target/debug");
if !path.exists() {
path = Path::new("../../target/debug");
}
gst::Registry::get().scan_path(path);
}
#[cfg(not(debug_assertions))]
{
use std::path::Path;
let mut path = Path::new("target/release");
if !path.exists() {
path = Path::new("../../target/release");
}
gst::Registry::get().scan_path(path);
}
let l = glib::MainLoop::new(None, false);
fn send_test_buffers(n_streams: u16, num_buffers: Option<i32>) {
let pipeline = gst::Pipeline::default();
for i in 0..n_streams {
let src = gst::ElementFactory::make("audiotestsrc")
.name(format!("audiotestsrc-{}", i).as_str())
let src = gst::ElementFactory::make("ts-audiotestsrc")
.name(format!("ts-audiotestsrc-{}", i).as_str())
.property("context-wait", 20u32)
.property("is-live", true)
.property("do-timestamp", true)
.build()
.unwrap();
src.set_property("is-live", true);
if let Some(num_buffers) = num_buffers {
src.set_property("num-buffers", num_buffers);
}
#[cfg(feature = "tuning")]
if i == 0 {
src.set_property("main-elem", true);
}
let sink = gst::ElementFactory::make("ts-udpsink")
.name(format!("udpsink-{}", i).as_str())
.property("clients", format!("127.0.0.1:{}", i + 5004))
.property("context-wait", 20u32)
.build()
.unwrap();
let elements = &[&src, &sink];
pipeline.add_many(elements).unwrap();
gst::Element::link_many(elements).unwrap();
}
run(pipeline);
}
fn send_rtp_buffers(n_streams: u16, num_buffers: Option<i32>) {
let pipeline = gst::Pipeline::default();
for i in 0..n_streams {
let src = gst::ElementFactory::make("ts-audiotestsrc")
.name(format!("ts-audiotestsrc-{}", i).as_str())
.property("context-wait", 20u32)
.property("is-live", true)
.property("do-timestamp", true)
.build()
.unwrap();
if let Some(num_buffers) = num_buffers {
src.set_property("num-buffers", num_buffers);
}
#[cfg(feature = "tuning")]
if i == 0 {
src.set_property("main-elem", true);
}
let enc = gst::ElementFactory::make("alawenc")
.name(format!("alawenc-{}", i).as_str())
@ -106,11 +149,11 @@ fn send_rtp_buffers(n_streams: u16) {
.name(format!("rtppcmapay-{}", i).as_str())
.build()
.unwrap();
let sink = gst::ElementFactory::make("ts-udpsink")
.name(format!("udpsink-{}", i).as_str())
.property("clients", format!("127.0.0.1:{}", i + 40000))
.property("context", "context-udpsink")
.property("context-wait", 20u32)
.property("clients", format!("127.0.0.1:{}", i + 5004))
.build()
.unwrap();
@ -119,6 +162,42 @@ fn send_rtp_buffers(n_streams: u16) {
gst::Element::link_many(elements).unwrap();
}
run(pipeline);
}
fn run(pipeline: gst::Pipeline) {
let l = glib::MainLoop::new(None, false);
let bus = pipeline.bus().unwrap();
let l_clone = l.clone();
bus.add_watch(move |_, msg| {
use gst::MessageView;
match msg.view() {
MessageView::Eos(_) => {
gst::info!(CAT, "Received eos");
l_clone.quit();
glib::Continue(false)
}
MessageView::Error(msg) => {
gst::error!(
CAT,
"Error from {:?}: {} ({:?})",
msg.src().map(|s| s.path_string()),
msg.error(),
msg.debug()
);
l_clone.quit();
glib::Continue(false)
}
_ => glib::Continue(true),
}
})
.expect("Failed to add bus watch");
pipeline.set_state(gst::State::Playing).unwrap();
l.run();
pipeline.set_state(gst::State::Null).unwrap();
}

View file

@ -0,0 +1,735 @@
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use futures::future::BoxFuture;
use futures::prelude::*;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use once_cell::sync::Lazy;
use std::mem::size_of;
use std::sync::Mutex;
use std::time::Duration;
#[cfg(feature = "tuning")]
use std::time::Instant;
use crate::runtime::prelude::*;
use crate::runtime::{self, task, timer, PadSrc, Task};
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"ts-audiotestsrc",
gst::DebugColorFlags::empty(),
Some("Thread-sharing audio test src"),
)
});
const DEFAULT_CONTEXT: &str = "";
const DEFAULT_CONTEXT_WAIT: Duration = Duration::ZERO;
const DEFAULT_BUFFER_DURATION: gst::ClockTime = gst::ClockTime::from_mseconds(10);
const DEFAULT_DO_TIMESTAMP: bool = false;
const DEFAULT_IS_LIVE: bool = false;
const DEFAULT_NUM_BUFFERS: i32 = -1;
const DEFAULT_CHANNELS: usize = 1;
const DEFAULT_FREQ: f32 = 440.0;
const DEFAULT_VOLUME: f32 = 0.8;
const DEFAULT_RATE: u32 = 44_100;
#[cfg(feature = "tuning")]
const RAMPUP_BUFFER_COUNT: u32 = 500;
#[cfg(feature = "tuning")]
const LOG_BUFFER_INTERVAL: u32 = 2000;
static DEFAULT_CAPS: Lazy<gst::Caps> = Lazy::new(|| {
gst_audio::AudioCapsBuilder::new_interleaved()
.format(gst_audio::AUDIO_FORMAT_S16)
.rate_range(8_000..i32::MAX)
.channels_range(1..i32::MAX)
.build()
});
#[derive(Debug, Clone)]
struct Settings {
context: String,
context_wait: Duration,
do_timestamp: bool,
is_live: bool,
buffer_duration: gst::ClockTime,
num_buffers: Option<u32>,
#[cfg(feature = "tuning")]
is_main_elem: bool,
}
impl Default for Settings {
fn default() -> Self {
Settings {
context: DEFAULT_CONTEXT.into(),
context_wait: DEFAULT_CONTEXT_WAIT,
do_timestamp: DEFAULT_DO_TIMESTAMP,
is_live: DEFAULT_IS_LIVE,
buffer_duration: DEFAULT_BUFFER_DURATION,
num_buffers: None,
#[cfg(feature = "tuning")]
is_main_elem: false,
}
}
}
#[derive(Clone, Debug)]
struct AudioTestSrcPadHandler;
impl PadSrcHandler for AudioTestSrcPadHandler {
type ElementImpl = AudioTestSrc;
fn src_query(self, pad: &gst::Pad, imp: &Self::ElementImpl, query: &mut gst::QueryRef) -> bool {
gst::debug!(CAT, obj: pad, "Received {query:?}");
if let gst::QueryViewMut::Latency(q) = query.view_mut() {
let settings = imp.settings.lock().unwrap();
let min_latency = if settings.is_live {
settings.buffer_duration
} else {
gst::ClockTime::ZERO
};
q.set(
settings.is_live,
min_latency,
min_latency
+ runtime::Context::current().map_or(gst::ClockTime::ZERO, |ctx| {
gst::ClockTime::try_from(ctx.wait_duration()).unwrap()
}),
);
return true;
}
gst::Pad::query_default(pad, Some(&*imp.obj()), query)
}
}
#[derive(Debug, Copy, Clone)]
enum Negotiation {
Unchanged,
Changed,
}
impl Negotiation {
fn has_changed(self) -> bool {
matches!(self, Negotiation::Changed)
}
}
#[derive(Debug)]
struct AudioTestSrcTask {
elem: super::AudioTestSrc,
buffer_pool: gst::BufferPool,
rate: u32,
channels: usize,
do_timestamp: bool,
is_live: bool,
buffer_duration: gst::ClockTime,
need_initial_events: bool,
step: f32,
accumulator: f32,
last_buffer_end: Option<gst::ClockTime>,
caps: gst::Caps,
buffer_count: u32,
num_buffers: Option<u32>,
#[cfg(feature = "tuning")]
is_main_elem: bool,
#[cfg(feature = "tuning")]
parked_duration_init: Option<Duration>,
#[cfg(feature = "tuning")]
log_start: Instant,
}
impl AudioTestSrcTask {
fn new(elem: super::AudioTestSrc) -> Self {
AudioTestSrcTask {
elem,
buffer_pool: gst::BufferPool::new(),
rate: DEFAULT_RATE,
channels: DEFAULT_CHANNELS,
do_timestamp: DEFAULT_DO_TIMESTAMP,
is_live: DEFAULT_IS_LIVE,
buffer_duration: DEFAULT_BUFFER_DURATION,
need_initial_events: true,
step: 0.0,
accumulator: 0.0,
last_buffer_end: None,
caps: gst::Caps::new_empty(),
buffer_count: 0,
num_buffers: None,
#[cfg(feature = "tuning")]
is_main_elem: false,
#[cfg(feature = "tuning")]
parked_duration_init: None,
#[cfg(feature = "tuning")]
log_start: Instant::now(),
}
}
async fn negotiate(&mut self) -> Result<Negotiation, gst::ErrorMessage> {
let imp = self.elem.imp();
let pad = imp.src_pad.gst_pad();
if !pad.check_reconfigure() {
return Ok(Negotiation::Unchanged);
}
let mut caps = pad.peer_query_caps(Some(&DEFAULT_CAPS));
gst::debug!(CAT, imp: imp, "Peer returned {caps:?}");
if caps.is_empty() {
pad.mark_reconfigure();
let err = gst::error_msg!(gst::CoreError::Pad, ["No common Caps"]);
gst::error!(CAT, imp: imp, "{err}");
return Err(err);
}
if caps.is_any() {
gst::debug!(CAT, imp: imp, "Using our own Caps");
caps = DEFAULT_CAPS.clone();
}
{
let caps = caps.make_mut();
let s = caps.structure_mut(0).ok_or_else(|| {
let err = gst::error_msg!(gst::CoreError::Pad, ["Invalid peer Caps structure"]);
gst::error!(CAT, imp: imp, "{err}");
err
})?;
s.fixate_field_nearest_int("rate", DEFAULT_RATE as i32);
self.rate = s.get::<i32>("rate").unwrap() as u32;
self.step = 2.0 * std::f32::consts::PI * DEFAULT_FREQ / (self.rate as f32);
s.fixate_field_nearest_int("channels", DEFAULT_CHANNELS as i32);
self.channels = s.get::<i32>("channels").unwrap() as usize;
if self.channels > 2 {
s.set::<gst::Bitmask>(
"channel-mask",
gst_audio::AudioChannelPosition::fallback_mask(self.channels as u32).into(),
);
}
}
caps.fixate();
gst::debug!(CAT, imp: imp, "fixated to {caps:?}");
imp.src_pad.push_event(gst::event::Caps::new(&caps)).await;
self.caps = caps;
Ok(Negotiation::Changed)
}
}
impl TaskImpl for AudioTestSrcTask {
type Item = gst::Buffer;
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
gst::log!(CAT, obj: self.elem, "Preparing Task");
let imp = self.elem.imp();
let settings = imp.settings.lock().unwrap();
self.do_timestamp = settings.do_timestamp;
self.is_live = settings.is_live;
self.buffer_duration = settings.buffer_duration;
self.num_buffers = settings.num_buffers;
#[cfg(feature = "tuning")]
{
self.is_main_elem = settings.is_main_elem;
}
future::ok(()).boxed()
}
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
async move {
gst::log!(CAT, obj: self.elem, "Starting Task");
if self.need_initial_events {
gst::debug!(CAT, obj: self.elem, "Pushing initial events");
let stream_id =
format!("{:08x}{:08x}", rand::random::<u32>(), rand::random::<u32>());
let stream_start_evt = gst::event::StreamStart::builder(&stream_id)
.group_id(gst::GroupId::next())
.build();
self.elem.imp().src_pad.push_event(stream_start_evt).await;
}
if self.negotiate().await?.has_changed() {
let bytes_per_buffer = (self.rate as u64)
* self.buffer_duration.mseconds()
* self.channels as u64
* size_of::<i16>() as u64
/ 1_000;
let mut pool_config = self.buffer_pool.config();
pool_config
.as_mut()
.set_params(Some(&self.caps), bytes_per_buffer as u32, 2, 6);
self.buffer_pool.set_config(pool_config).unwrap();
}
assert!(!self.caps.is_empty());
self.buffer_pool.set_active(true).unwrap();
if self.need_initial_events {
let segment_evt =
gst::event::Segment::new(&gst::FormattedSegment::<gst::format::Time>::new());
self.elem.imp().src_pad.push_event(segment_evt).await;
self.need_initial_events = false;
}
self.buffer_count = 0;
#[cfg(feature = "tuning")]
if self.is_main_elem {
self.parked_duration_init = None;
}
Ok(())
}
.boxed()
}
fn pause(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
gst::log!(CAT, obj: self.elem, "Pausing Task");
self.buffer_pool.set_active(false).unwrap();
future::ok(()).boxed()
}
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
gst::log!(CAT, obj: self.elem, "Stopping Task");
self.need_initial_events = true;
self.accumulator = 0.0;
self.last_buffer_end = None;
future::ok(()).boxed()
}
fn try_next(&mut self) -> BoxFuture<'_, Result<gst::Buffer, gst::FlowError>> {
let mut buffer = match self.buffer_pool.acquire_buffer(None) {
Ok(buffer) => buffer,
Err(err) => {
gst::error!(CAT, obj: self.elem, "Failed to acquire buffer {}", err);
return future::err(err).boxed();
}
};
let buffer_mut = buffer.get_mut().unwrap();
let start = if self.is_live | self.do_timestamp {
self.last_buffer_end
.or_else(|| self.elem.current_running_time())
} else {
None
};
{
use std::io::Write;
let mut mapped = buffer_mut.map_writable().unwrap();
let slice = mapped.as_mut_slice();
slice
.chunks_mut(self.channels * size_of::<i16>())
.for_each(|frame| {
let sample = ((self.accumulator.sin() * DEFAULT_VOLUME * (i16::MAX as f32))
as i16)
.to_ne_bytes();
frame.chunks_mut(size_of::<i16>()).for_each(|mut channel| {
let _ = channel.write(&sample).unwrap();
});
self.accumulator += self.step;
if self.accumulator >= 2.0 * std::f32::consts::PI {
self.accumulator = -2.0 * std::f32::consts::PI;
}
});
}
if self.do_timestamp {
buffer_mut.set_dts(start);
buffer_mut.set_duration(self.buffer_duration);
}
self.last_buffer_end = start.opt_add(self.buffer_duration);
async move {
if self.is_live {
if let Some(delay) = self
.last_buffer_end
.unwrap()
.checked_sub(self.elem.current_running_time().unwrap())
{
// Wait for all samples to fit in last time slice
timer::delay_for_at_least(delay.into()).await;
}
} else {
// Let the scheduler share time with other tasks
runtime::executor::yield_now().await;
}
Ok(buffer)
}
.boxed()
}
fn handle_item(&mut self, buffer: gst::Buffer) -> BoxFuture<'_, Result<(), gst::FlowError>> {
async move {
let imp = self.elem.imp();
gst::debug!(CAT, imp: imp, "Pushing {buffer:?}");
imp.src_pad.push(buffer).await?;
gst::log!(CAT, imp: imp, "Successfully pushed buffer");
self.buffer_count += 1;
#[cfg(feature = "tuning")]
if self.is_main_elem {
if let Some(parked_duration_init) = self.parked_duration_init {
if self.buffer_count % LOG_BUFFER_INTERVAL == 0 {
let parked_duration =
runtime::Context::current().unwrap().parked_duration()
- parked_duration_init;
gst::info!(
CAT,
"Parked: {:5.2?}%",
parked_duration.as_nanos() as f32 * 100.0
/ self.log_start.elapsed().as_nanos() as f32,
);
}
} else if self.buffer_count == RAMPUP_BUFFER_COUNT {
self.parked_duration_init =
Some(runtime::Context::current().unwrap().parked_duration());
self.log_start = Instant::now();
gst::info!(CAT, "Ramp up complete");
}
}
if self.num_buffers.opt_eq(self.buffer_count) == Some(true) {
return Err(gst::FlowError::Eos);
}
Ok(())
}
.boxed()
}
fn handle_loop_error(&mut self, err: gst::FlowError) -> BoxFuture<'_, task::Trigger> {
async move {
match err {
gst::FlowError::Flushing => {
gst::debug!(CAT, obj: self.elem, "Flushing");
task::Trigger::FlushStart
}
gst::FlowError::Eos => {
gst::debug!(CAT, obj: self.elem, "EOS");
self.elem
.imp()
.src_pad
.push_event(gst::event::Eos::new())
.await;
task::Trigger::Stop
}
err => {
gst::error!(CAT, obj: self.elem, "Got error {err}");
gst::element_error!(
&self.elem,
gst::StreamError::Failed,
("Internal data stream error"),
["streaming stopped, reason {}", err]
);
task::Trigger::Error
}
}
}
.boxed()
}
}
#[derive(Debug)]
pub struct AudioTestSrc {
src_pad: PadSrc,
task: Task,
settings: Mutex<Settings>,
}
impl AudioTestSrc {
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
gst::debug!(CAT, imp: self, "Preparing");
let settings = self.settings.lock().unwrap();
let context =
runtime::Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
gst::error_msg!(
gst::ResourceError::OpenRead,
["Failed to acquire Context: {}", err]
)
})?;
drop(settings);
self.task
.prepare(AudioTestSrcTask::new(self.obj().clone()), context)
.block_on()?;
gst::debug!(CAT, imp: self, "Prepared");
Ok(())
}
fn unprepare(&self) {
gst::debug!(CAT, imp: self, "Unpreparing");
self.task.unprepare().block_on().unwrap();
gst::debug!(CAT, imp: self, "Unprepared");
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
gst::debug!(CAT, imp: self, "Stopping");
self.task.stop().block_on()?;
gst::debug!(CAT, imp: self, "Stopped");
Ok(())
}
fn start(&self) -> Result<(), gst::ErrorMessage> {
gst::debug!(CAT, imp: self, "Starting");
self.task.start().block_on()?;
gst::debug!(CAT, imp: self, "Started");
Ok(())
}
fn pause(&self) -> Result<(), gst::ErrorMessage> {
gst::debug!(CAT, imp: self, "Pausing");
self.task.pause().block_on()?;
gst::debug!(CAT, imp: self, "Paused");
Ok(())
}
}
#[glib::object_subclass]
impl ObjectSubclass for AudioTestSrc {
const NAME: &'static str = "TsAudioTestSrc";
type Type = super::AudioTestSrc;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
Self {
src_pad: PadSrc::new(
gst::Pad::from_template(&klass.pad_template("src").unwrap(), Some("src")),
AudioTestSrcPadHandler,
),
task: Task::default(),
settings: Default::default(),
}
}
}
impl ObjectImpl for AudioTestSrc {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecString::builder("context")
.nick("Context")
.blurb("Context name to share threads with")
.default_value(Some(DEFAULT_CONTEXT))
.build(),
glib::ParamSpecUInt::builder("context-wait")
.nick("Context Wait")
.blurb("Throttle poll loop to run at most once every this many ms")
.maximum(1000)
.default_value(DEFAULT_CONTEXT_WAIT.as_millis() as u32)
.build(),
glib::ParamSpecBoolean::builder("do-timestamp")
.nick("Do timestamp")
.blurb("Apply current stream time to buffers")
.build(),
glib::ParamSpecBoolean::builder("is-live")
.nick("Is live")
.blurb("Whether to act as a live source")
.build(),
glib::ParamSpecUInt::builder("buffer-duration")
.nick("Buffer duration")
.blurb("Buffer duration in ms")
.default_value(DEFAULT_BUFFER_DURATION.mseconds() as u32)
.build(),
glib::ParamSpecInt::builder("num-buffers")
.nick("Num Buffers")
.blurb("Number of buffers to output before sending EOS (-1 = unlimited)")
.minimum(-1i32)
.default_value(DEFAULT_NUM_BUFFERS)
.build(),
#[cfg(feature = "tuning")]
glib::ParamSpecBoolean::builder("main-elem")
.nick("Main Element")
.blurb("Declare this element as the main one")
.write_only()
.build(),
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
let mut settings = self.settings.lock().unwrap();
match pspec.name() {
"context" => {
settings.context = value
.get::<Option<String>>()
.unwrap()
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
}
"context-wait" => {
settings.context_wait = Duration::from_millis(value.get::<u32>().unwrap().into());
}
"do-timestamp" => {
settings.do_timestamp = value.get::<bool>().unwrap();
}
"is-live" => {
settings.is_live = value.get::<bool>().unwrap();
}
"buffer-duration" => {
settings.buffer_duration = (value.get::<u32>().unwrap() as u64).mseconds();
}
"num-buffers" => {
let value = value.get::<i32>().unwrap();
settings.num_buffers = if value > 0 { Some(value as u32) } else { None };
}
#[cfg(feature = "tuning")]
"main-elem" => {
settings.is_main_elem = value.get::<bool>().unwrap();
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let settings = self.settings.lock().unwrap();
match pspec.name() {
"context" => settings.context.to_value(),
"context-wait" => (settings.context_wait.as_millis() as u32).to_value(),
"do-timestamp" => settings.do_timestamp.to_value(),
"is-live" => settings.is_live.to_value(),
"buffer-duration" => (settings.buffer_duration.mseconds() as u32).to_value(),
"num-buffers" => settings
.num_buffers
.and_then(|val| val.try_into().ok())
.unwrap_or(-1i32)
.to_value(),
#[cfg(feature = "tuning")]
"main-elem" => settings.is_main_elem.to_value(),
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(self.src_pad.gst_pad()).unwrap();
obj.set_element_flags(gst::ElementFlags::SOURCE);
}
}
impl GstObjectImpl for AudioTestSrc {}
impl ElementImpl for AudioTestSrc {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Thread-sharing audio test source",
"Source/Test",
"Thread-sharing audio test source",
"François Laignel <fengalin@free.fr>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&DEFAULT_CAPS,
)
.unwrap();
vec![src_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {transition:?}");
match transition {
gst::StateChange::NullToReady => {
self.prepare().map_err(|err| {
self.post_error_message(err);
gst::StateChangeError
})?;
}
gst::StateChange::PlayingToPaused => {
self.pause().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::ReadyToNull => {
self.unprepare();
}
_ => (),
}
let mut success = self.parent_change_state(transition)?;
match transition {
gst::StateChange::ReadyToPaused => {
self.pause().map_err(|_| gst::StateChangeError)?;
success = gst::StateChangeSuccess::NoPreroll;
}
gst::StateChange::PausedToPlaying => {
self.start().map_err(|_| gst::StateChangeError)?;
}
gst::StateChange::PlayingToPaused => {
success = gst::StateChangeSuccess::NoPreroll;
}
gst::StateChange::PausedToReady => {
self.stop().map_err(|_| gst::StateChangeError)?;
}
_ => (),
}
Ok(success)
}
}

View file

@ -0,0 +1,17 @@
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct AudioTestSrc(ObjectSubclass<imp::AudioTestSrc>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"ts-audiotestsrc",
gst::Rank::None,
AudioTestSrc::static_type(),
)
}

View file

@ -57,27 +57,27 @@ pub const RTP_JITTER_BUFFER_MODE_BUFFER: RTPJitterBufferMode = 2;
pub const RTP_JITTER_BUFFER_MODE_SYNCED: RTPJitterBufferMode = 4;
extern "C" {
pub fn rtp_jitter_buffer_new() -> *mut RTPJitterBuffer;
pub fn rtp_jitter_buffer_get_type() -> GType;
pub fn ts_rtp_jitter_buffer_new() -> *mut RTPJitterBuffer;
pub fn ts_rtp_jitter_buffer_get_type() -> GType;
#[allow(dead_code)]
pub fn rtp_jitter_buffer_get_mode(jbuf: *mut RTPJitterBuffer) -> RTPJitterBufferMode;
pub fn ts_rtp_jitter_buffer_get_mode(jbuf: *mut RTPJitterBuffer) -> RTPJitterBufferMode;
#[allow(dead_code)]
pub fn rtp_jitter_buffer_set_mode(jbuf: *mut RTPJitterBuffer, mode: RTPJitterBufferMode);
pub fn ts_rtp_jitter_buffer_set_mode(jbuf: *mut RTPJitterBuffer, mode: RTPJitterBufferMode);
#[allow(dead_code)]
pub fn rtp_jitter_buffer_get_delay(jbuf: *mut RTPJitterBuffer) -> GstClockTime;
pub fn rtp_jitter_buffer_set_delay(jbuf: *mut RTPJitterBuffer, delay: GstClockTime);
pub fn rtp_jitter_buffer_set_clock_rate(jbuf: *mut RTPJitterBuffer, clock_rate: c_uint);
pub fn ts_rtp_jitter_buffer_get_delay(jbuf: *mut RTPJitterBuffer) -> GstClockTime;
pub fn ts_rtp_jitter_buffer_set_delay(jbuf: *mut RTPJitterBuffer, delay: GstClockTime);
pub fn ts_rtp_jitter_buffer_set_clock_rate(jbuf: *mut RTPJitterBuffer, clock_rate: c_uint);
#[allow(dead_code)]
pub fn rtp_jitter_buffer_get_clock_rate(jbuf: *mut RTPJitterBuffer) -> c_uint;
pub fn rtp_jitter_buffer_reset_skew(jbuf: *mut RTPJitterBuffer);
pub fn ts_rtp_jitter_buffer_get_clock_rate(jbuf: *mut RTPJitterBuffer) -> c_uint;
pub fn ts_rtp_jitter_buffer_reset_skew(jbuf: *mut RTPJitterBuffer);
pub fn rtp_jitter_buffer_flush(jbuf: *mut RTPJitterBuffer, free_func: glib::ffi::GFunc);
pub fn rtp_jitter_buffer_find_earliest(
pub fn ts_rtp_jitter_buffer_flush(jbuf: *mut RTPJitterBuffer, free_func: glib::ffi::GFunc);
pub fn ts_rtp_jitter_buffer_find_earliest(
jbuf: *mut RTPJitterBuffer,
pts: *mut GstClockTime,
seqnum: *mut c_uint,
);
pub fn rtp_jitter_buffer_calculate_pts(
pub fn ts_rtp_jitter_buffer_calculate_pts(
jbuf: *mut RTPJitterBuffer,
dts: GstClockTime,
estimated_dts: gboolean,
@ -86,30 +86,30 @@ extern "C" {
gap: c_int,
is_rtx: gboolean,
) -> GstClockTime;
pub fn rtp_jitter_buffer_insert(
pub fn ts_rtp_jitter_buffer_insert(
jbuf: *mut RTPJitterBuffer,
item: *mut RTPJitterBufferItem,
head: *mut gboolean,
percent: *mut c_int,
) -> gboolean;
pub fn rtp_jitter_buffer_pop(
pub fn ts_rtp_jitter_buffer_pop(
jbuf: *mut RTPJitterBuffer,
percent: *mut c_int,
) -> *mut RTPJitterBufferItem;
pub fn rtp_jitter_buffer_peek(jbuf: *mut RTPJitterBuffer) -> *mut RTPJitterBufferItem;
pub fn ts_rtp_jitter_buffer_peek(jbuf: *mut RTPJitterBuffer) -> *mut RTPJitterBufferItem;
pub fn gst_rtp_packet_rate_ctx_reset(ctx: *mut RTPPacketRateCtx, clock_rate: c_int);
pub fn gst_rtp_packet_rate_ctx_update(
pub fn ts_gst_rtp_packet_rate_ctx_reset(ctx: *mut RTPPacketRateCtx, clock_rate: c_int);
pub fn ts_gst_rtp_packet_rate_ctx_update(
ctx: *mut RTPPacketRateCtx,
seqnum: c_ushort,
ts: c_uint,
) -> c_uint;
pub fn gst_rtp_packet_rate_ctx_get_max_dropout(
pub fn ts_gst_rtp_packet_rate_ctx_get_max_dropout(
ctx: *mut RTPPacketRateCtx,
time_ms: c_int,
) -> c_uint;
#[allow(dead_code)]
pub fn gst_rtp_packet_rate_ctx_get_max_misorder(
pub fn ts_gst_rtp_packet_rate_ctx_get_max_misorder(
ctx: *mut RTPPacketRateCtx,
time_ms: c_int,
) -> c_uint;

View file

@ -28,7 +28,7 @@ use gst_rtp::RTPBuffer;
use once_cell::sync::Lazy;
use std::cmp::{max, min, Ordering};
use std::cmp::Ordering;
use std::collections::{BTreeSet, VecDeque};
use std::mem;
use std::sync::Arc;
@ -381,7 +381,7 @@ impl SinkHandler {
let caps = element
.emit_by_name::<Option<gst::Caps>>("request-pt-map", &[&(pt as u32)])
.ok_or_else(|| {
gst::error!(CAT, obj: pad, "Signal 'request-pt-map' retuned None");
gst::error!(CAT, obj: pad, "Signal 'request-pt-map' returned None");
gst::FlowError::Error
})?;
let mut state = jb.state.lock().unwrap();
@ -412,7 +412,7 @@ impl SinkHandler {
}
if let Some(last_in_seqnum) = inner.last_in_seqnum {
let gap = gst_rtp::compare_seqnum(last_in_seqnum as u16, seq);
let gap = gst_rtp::compare_seqnum(last_in_seqnum, seq);
if gap == 1 {
self.calculate_packet_spacing(inner, &mut state, rtptime, pts);
} else {
@ -463,7 +463,7 @@ impl SinkHandler {
state.equidistant += 1;
}
state.equidistant = min(max(state.equidistant, -7), 7);
state.equidistant = state.equidistant.clamp(-7, 7);
inner.last_rtptime = Some(rtptime);
@ -679,7 +679,7 @@ impl SrcHandler {
// FIXME reason why we can expect Some for the 2 lines below
let mut last_popped_pts = state.last_popped_pts.unwrap();
let interval = pts.into().unwrap().saturating_sub(last_popped_pts);
let spacing = interval / (gap as u64 + 1);
let spacing = interval / (gap + 1);
*discont = true;
@ -1259,7 +1259,7 @@ impl JitterBuffer {
self.task
.prepare(
JitterBufferTask::new(&*self.obj(), &self.src_pad_handler, &self.sink_pad_handler),
JitterBufferTask::new(&self.obj(), &self.src_pad_handler, &self.sink_pad_handler),
context,
)
.block_on()?;

View file

@ -32,7 +32,7 @@ glib::wrapper! {
pub struct RTPJitterBuffer(Object<ffi::RTPJitterBuffer>);
match fn {
type_ => || ffi::rtp_jitter_buffer_get_type(),
type_ => || ffi::ts_rtp_jitter_buffer_get_type(),
}
}
@ -180,25 +180,25 @@ impl RTPPacketRateCtx {
pub fn new() -> RTPPacketRateCtx {
unsafe {
let mut ptr = std::mem::MaybeUninit::uninit();
ffi::gst_rtp_packet_rate_ctx_reset(ptr.as_mut_ptr(), -1);
ffi::ts_gst_rtp_packet_rate_ctx_reset(ptr.as_mut_ptr(), -1);
RTPPacketRateCtx(Box::new(ptr.assume_init()))
}
}
pub fn reset(&mut self, clock_rate: i32) {
unsafe { ffi::gst_rtp_packet_rate_ctx_reset(&mut *self.0, clock_rate) }
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_reset(&mut *self.0, clock_rate) }
}
pub fn update(&mut self, seqnum: u16, ts: u32) -> u32 {
unsafe { ffi::gst_rtp_packet_rate_ctx_update(&mut *self.0, seqnum, ts) }
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_update(&mut *self.0, seqnum, ts) }
}
pub fn max_dropout(&mut self, time_ms: i32) -> u32 {
unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_dropout(&mut *self.0, time_ms) }
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_get_max_dropout(&mut *self.0, time_ms) }
}
pub fn max_misorder(&mut self, time_ms: i32) -> u32 {
unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_misorder(&mut *self.0, time_ms) }
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_get_max_misorder(&mut *self.0, time_ms) }
}
}
@ -219,38 +219,38 @@ pub enum RTPJitterBufferMode {
impl RTPJitterBuffer {
pub fn new() -> RTPJitterBuffer {
unsafe { from_glib_full(ffi::rtp_jitter_buffer_new()) }
unsafe { from_glib_full(ffi::ts_rtp_jitter_buffer_new()) }
}
#[allow(dead_code)]
pub fn mode(&self) -> RTPJitterBufferMode {
unsafe { from_glib(ffi::rtp_jitter_buffer_get_mode(self.to_glib_none().0)) }
unsafe { from_glib(ffi::ts_rtp_jitter_buffer_get_mode(self.to_glib_none().0)) }
}
#[allow(dead_code)]
pub fn set_mode(&self, mode: RTPJitterBufferMode) {
unsafe { ffi::rtp_jitter_buffer_set_mode(self.to_glib_none().0, mode.into_glib()) }
unsafe { ffi::ts_rtp_jitter_buffer_set_mode(self.to_glib_none().0, mode.into_glib()) }
}
#[allow(dead_code)]
pub fn delay(&self) -> gst::ClockTime {
unsafe {
try_from_glib(ffi::rtp_jitter_buffer_get_delay(self.to_glib_none().0))
try_from_glib(ffi::ts_rtp_jitter_buffer_get_delay(self.to_glib_none().0))
.expect("undefined delay")
}
}
pub fn set_delay(&self, delay: gst::ClockTime) {
unsafe { ffi::rtp_jitter_buffer_set_delay(self.to_glib_none().0, delay.into_glib()) }
unsafe { ffi::ts_rtp_jitter_buffer_set_delay(self.to_glib_none().0, delay.into_glib()) }
}
pub fn set_clock_rate(&self, clock_rate: u32) {
unsafe { ffi::rtp_jitter_buffer_set_clock_rate(self.to_glib_none().0, clock_rate) }
unsafe { ffi::ts_rtp_jitter_buffer_set_clock_rate(self.to_glib_none().0, clock_rate) }
}
#[allow(dead_code)]
pub fn clock_rate(&self) -> u32 {
unsafe { ffi::rtp_jitter_buffer_get_clock_rate(self.to_glib_none().0) }
unsafe { ffi::ts_rtp_jitter_buffer_get_clock_rate(self.to_glib_none().0) }
}
pub fn calculate_pts(
@ -263,7 +263,7 @@ impl RTPJitterBuffer {
is_rtx: bool,
) -> Option<gst::ClockTime> {
unsafe {
from_glib(ffi::rtp_jitter_buffer_calculate_pts(
from_glib(ffi::ts_rtp_jitter_buffer_calculate_pts(
self.to_glib_none().0,
dts.into().into_glib(),
estimated_dts.into_glib(),
@ -280,7 +280,7 @@ impl RTPJitterBuffer {
let mut head = mem::MaybeUninit::uninit();
let mut percent = mem::MaybeUninit::uninit();
let ptr = item.0.take().expect("Invalid wrapper");
let ret: bool = from_glib(ffi::rtp_jitter_buffer_insert(
let ret: bool = from_glib(ffi::ts_rtp_jitter_buffer_insert(
self.to_glib_none().0,
ptr.as_ptr(),
head.as_mut_ptr(),
@ -298,7 +298,7 @@ impl RTPJitterBuffer {
let mut pts = mem::MaybeUninit::uninit();
let mut seqnum = mem::MaybeUninit::uninit();
ffi::rtp_jitter_buffer_find_earliest(
ffi::ts_rtp_jitter_buffer_find_earliest(
self.to_glib_none().0,
pts.as_mut_ptr(),
seqnum.as_mut_ptr(),
@ -319,7 +319,7 @@ impl RTPJitterBuffer {
pub fn pop(&self) -> (Option<RTPJitterBufferItem>, i32) {
unsafe {
let mut percent = mem::MaybeUninit::uninit();
let item = ffi::rtp_jitter_buffer_pop(self.to_glib_none().0, percent.as_mut_ptr());
let item = ffi::ts_rtp_jitter_buffer_pop(self.to_glib_none().0, percent.as_mut_ptr());
(
if item.is_null() {
@ -334,7 +334,7 @@ impl RTPJitterBuffer {
pub fn peek(&self) -> (Option<gst::ClockTime>, Option<u16>) {
unsafe {
let item = ffi::rtp_jitter_buffer_peek(self.to_glib_none().0);
let item = ffi::ts_rtp_jitter_buffer_peek(self.to_glib_none().0);
if item.is_null() {
(None, None)
} else {
@ -356,12 +356,12 @@ impl RTPJitterBuffer {
}
unsafe {
ffi::rtp_jitter_buffer_flush(self.to_glib_none().0, Some(free_item));
ffi::ts_rtp_jitter_buffer_flush(self.to_glib_none().0, Some(free_item));
}
}
pub fn reset_skew(&self) {
unsafe { ffi::rtp_jitter_buffer_reset_skew(self.to_glib_none().0) }
unsafe { ffi::ts_rtp_jitter_buffer_reset_skew(self.to_glib_none().0) }
}
}

View file

@ -42,10 +42,10 @@ enum
};
/* GObject vmethods */
static void rtp_jitter_buffer_finalize (GObject * object);
static void ts_rtp_jitter_buffer_finalize (GObject * object);
GType
rtp_jitter_buffer_mode_get_type (void)
ts_rtp_jitter_buffer_mode_get_type (void)
{
static GType jitter_buffer_mode_type = 0;
static const GEnumValue jitter_buffer_modes[] = {
@ -67,36 +67,36 @@ rtp_jitter_buffer_mode_get_type (void)
/* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */
G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT);
G_DEFINE_TYPE (TsRTPJitterBuffer, ts_rtp_jitter_buffer, G_TYPE_OBJECT);
static void
rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass)
ts_rtp_jitter_buffer_class_init (TsRTPJitterBufferClass * klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
gobject_class->finalize = rtp_jitter_buffer_finalize;
gobject_class->finalize = ts_rtp_jitter_buffer_finalize;
GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0,
"RTP Jitter Buffer");
}
static void
rtp_jitter_buffer_init (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_init (TsRTPJitterBuffer * jbuf)
{
g_mutex_init (&jbuf->clock_lock);
jbuf->packets = g_queue_new ();
jbuf->mode = RTP_JITTER_BUFFER_MODE_SLAVE;
rtp_jitter_buffer_reset_skew (jbuf);
ts_rtp_jitter_buffer_reset_skew (jbuf);
}
static void
rtp_jitter_buffer_finalize (GObject * object)
ts_rtp_jitter_buffer_finalize (GObject * object)
{
RTPJitterBuffer *jbuf;
TsRTPJitterBuffer *jbuf;
RTPJitterBufferItem *item;
jbuf = RTP_JITTER_BUFFER_CAST (object);
@ -127,7 +127,7 @@ rtp_jitter_buffer_finalize (GObject * object)
g_mutex_clear (&jbuf->clock_lock);
G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object);
G_OBJECT_CLASS (ts_rtp_jitter_buffer_parent_class)->finalize (object);
}
/**
@ -137,10 +137,10 @@ rtp_jitter_buffer_finalize (GObject * object)
*
* Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage.
*/
RTPJitterBuffer *
rtp_jitter_buffer_new (void)
TsRTPJitterBuffer *
ts_rtp_jitter_buffer_new (void)
{
RTPJitterBuffer *jbuf;
TsRTPJitterBuffer *jbuf;
jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL);
@ -156,7 +156,7 @@ rtp_jitter_buffer_new (void)
* Returns: the current jitterbuffer mode.
*/
RTPJitterBufferMode
rtp_jitter_buffer_get_mode (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_mode (TsRTPJitterBuffer * jbuf)
{
return jbuf->mode;
}
@ -169,20 +169,20 @@ rtp_jitter_buffer_get_mode (RTPJitterBuffer * jbuf)
* Set the buffering and clock slaving algorithm used in the @jbuf.
*/
void
rtp_jitter_buffer_set_mode (RTPJitterBuffer * jbuf, RTPJitterBufferMode mode)
ts_rtp_jitter_buffer_set_mode (TsRTPJitterBuffer * jbuf, RTPJitterBufferMode mode)
{
jbuf->mode = mode;
}
GstClockTime
rtp_jitter_buffer_get_delay (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_delay (RTPJitterBuffer * jbuf)
{
g_print ("%p getting delay (%" G_GUINT64_FORMAT ")\n", jbuf, jbuf->delay);
return jbuf->delay;
}
void
rtp_jitter_buffer_set_delay (RTPJitterBuffer * jbuf, GstClockTime delay)
ts_rtp_jitter_buffer_set_delay (TsRTPJitterBuffer * jbuf, GstClockTime delay)
{
jbuf->delay = delay;
jbuf->low_level = (delay * 15) / 100;
@ -203,13 +203,13 @@ rtp_jitter_buffer_set_delay (RTPJitterBuffer * jbuf, GstClockTime delay)
* Set the clock rate in the jitterbuffer.
*/
void
rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer * jbuf, guint32 clock_rate)
ts_rtp_jitter_buffer_set_clock_rate (TsRTPJitterBuffer * jbuf, guint32 clock_rate)
{
if (jbuf->clock_rate != clock_rate) {
GST_DEBUG ("Clock rate changed from %" G_GUINT32_FORMAT " to %"
G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate);
jbuf->clock_rate = clock_rate;
rtp_jitter_buffer_reset_skew (jbuf);
ts_rtp_jitter_buffer_reset_skew (jbuf);
}
}
@ -222,7 +222,7 @@ rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer * jbuf, guint32 clock_rate)
* Returns: the current clock-rate
*/
guint32
rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
{
return jbuf->clock_rate;
}
@ -253,7 +253,7 @@ media_clock_synced_cb (GstClock * clock G_GNUC_UNUSED,
*
*/
void
rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
ts_rtp_jitter_buffer_set_media_clock (TsRTPJitterBuffer * jbuf, GstClock * clock,
guint64 clock_offset)
{
g_mutex_lock (&jbuf->clock_lock);
@ -295,7 +295,7 @@ rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
*
*/
void
rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
ts_rtp_jitter_buffer_set_pipeline_clock (TsRTPJitterBuffer * jbuf, GstClock * clock)
{
g_mutex_lock (&jbuf->clock_lock);
if (jbuf->pipeline_clock)
@ -319,13 +319,13 @@ rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
}
gboolean
rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer * jbuf)
{
return jbuf->rfc7273_sync;
}
void
rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer * jbuf,
ts_rtp_jitter_buffer_set_rfc7273_sync (TsRTPJitterBuffer * jbuf,
gboolean rfc7273_sync)
{
jbuf->rfc7273_sync = rfc7273_sync;
@ -338,7 +338,7 @@ rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer * jbuf,
* Reset the skew calculations in @jbuf.
*/
void
rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_reset_skew (TsRTPJitterBuffer * jbuf)
{
jbuf->base_time = -1;
jbuf->base_rtptime = -1;
@ -365,13 +365,13 @@ rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
* Enable or disable buffering on @jbuf.
*/
void
rtp_jitter_buffer_disable_buffering (RTPJitterBuffer * jbuf, gboolean disabled)
ts_rtp_jitter_buffer_disable_buffering (TsRTPJitterBuffer * jbuf, gboolean disabled)
{
jbuf->buffering_disabled = disabled;
}
static void
rtp_jitter_buffer_resync (RTPJitterBuffer * jbuf, GstClockTime time,
ts_rtp_jitter_buffer_resync (TsRTPJitterBuffer * jbuf, GstClockTime time,
GstClockTime gstrtptime, guint64 ext_rtptime, gboolean reset_skew)
{
jbuf->base_time = time;
@ -438,7 +438,7 @@ get_buffer_level (RTPJitterBuffer * jbuf)
}
static void
update_buffer_level (RTPJitterBuffer * jbuf, gint * percent)
update_buffer_level (TsRTPJitterBuffer * jbuf, gint * percent)
{
gboolean post = FALSE;
guint64 level;
@ -587,7 +587,7 @@ calculate_skew (RTPJitterBuffer * jbuf, guint64 ext_rtptime,
if (ABS (delta - jbuf->skew) > GST_SECOND) {
GST_WARNING ("delta - skew: %" GST_TIME_FORMAT " too big, reset skew",
GST_TIME_ARGS (ABS (delta - jbuf->skew)));
rtp_jitter_buffer_resync (jbuf, time, gstrtptime, ext_rtptime, TRUE);
ts_rtp_jitter_buffer_resync (jbuf, time, gstrtptime, ext_rtptime, TRUE);
send_diff = 0;
delta = 0;
gap = 0;
@ -692,7 +692,7 @@ no_skew:
}
static void
queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item)
queue_do_insert (TsRTPJitterBuffer * jbuf, GList * list, GList * item)
{
GQueue *queue = jbuf->packets;
@ -714,7 +714,7 @@ queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item)
}
GstClockTime
rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
ts_rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
gboolean estimated_dts, guint32 rtptime, GstClockTime base_time,
gint gap, gboolean is_rtx)
{
@ -742,7 +742,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
/* reset even if we don't have valid incoming time;
* still better than producing possibly very bogus output timestamp */
GST_WARNING ("rtp delta too big, reset skew");
rtp_jitter_buffer_reset_skew (jbuf);
ts_rtp_jitter_buffer_reset_skew (jbuf);
} else {
GST_WARNING ("rtp delta too big: ignore rtx packet");
media_clock = NULL;
@ -823,7 +823,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
}
GST_INFO ("resync to time %" GST_TIME_FORMAT ", rtptime %"
GST_TIME_FORMAT, GST_TIME_ARGS (dts), GST_TIME_ARGS (gstrtptime));
rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, FALSE);
ts_rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, FALSE);
}
GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
@ -973,7 +973,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
GST_DEBUG ("out %" GST_TIME_FORMAT " + %" G_GUINT64_FORMAT " < time %"
GST_TIME_FORMAT ", reset jitterbuffer and discard", GST_TIME_ARGS (pts),
jbuf->delay, GST_TIME_ARGS (dts));
rtp_jitter_buffer_reset_skew (jbuf);
ts_rtp_jitter_buffer_reset_skew (jbuf);
pts = GST_CLOCK_TIME_NONE;
goto done;
}
@ -1009,7 +1009,7 @@ done:
* Returns: %FALSE if a packet with the same number already existed.
*/
gboolean
rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
ts_rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
gboolean * head, gint * percent)
{
GList *list, *event = NULL;
@ -1102,7 +1102,7 @@ duplicate:
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
*/
RTPJitterBufferItem *
rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf, gint * percent)
ts_rtp_jitter_buffer_pop (TsRTPJitterBuffer * jbuf, gint * percent)
{
GList *item = NULL;
GQueue *queue;
@ -1142,7 +1142,7 @@ rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf, gint * percent)
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
*/
RTPJitterBufferItem *
rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_peek (TsRTPJitterBuffer * jbuf)
{
g_return_val_if_fail (jbuf != NULL, NULL);
@ -1158,7 +1158,7 @@ rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
* Flush all packets from the jitterbuffer.
*/
void
rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf, GFunc free_func,
ts_rtp_jitter_buffer_flush (TsRTPJitterBuffer * jbuf, GFunc free_func,
gpointer user_data)
{
GList *item;
@ -1180,7 +1180,7 @@ rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf, GFunc free_func,
* Returns: the buffering state of @jbuf
*/
gboolean
rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
{
return jbuf->buffering && !jbuf->buffering_disabled;
}
@ -1193,7 +1193,7 @@ rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
* Forces @jbuf to go into the buffering state.
*/
void
rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering)
ts_rtp_jitter_buffer_set_buffering (TsRTPJitterBuffer * jbuf, gboolean buffering)
{
jbuf->buffering = buffering;
}
@ -1207,7 +1207,7 @@ rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering)
* Returns: the buffering percent
*/
gint
rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
{
gint percent;
guint64 level;
@ -1234,7 +1234,7 @@ rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
* Returns: The number of packets in @jbuf.
*/
guint
rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
{
g_return_val_if_fail (jbuf != NULL, 0);
@ -1251,7 +1251,7 @@ rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
* Returns: The difference expressed in the timestamp units of the packets.
*/
guint32
rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
{
guint64 high_ts, low_ts;
RTPJitterBufferItem *high_buf, *low_buf;
@ -1288,7 +1288,7 @@ rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
* Returns: The difference expressed in seqnum.
*/
static guint16
rtp_jitter_buffer_get_seqnum_diff (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_get_seqnum_diff (RTPJitterBuffer * jbuf)
{
guint32 high_seqnum, low_seqnum;
RTPJitterBufferItem *high_buf, *low_buf;
@ -1338,7 +1338,7 @@ rtp_jitter_buffer_get_seqnum_diff (RTPJitterBuffer * jbuf)
* @last_rtptime.
*/
void
rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
ts_rtp_jitter_buffer_get_sync (TsRTPJitterBuffer * jbuf, guint64 * rtptime,
guint64 * timestamp, guint32 * clock_rate, guint64 * last_rtptime)
{
if (rtptime)
@ -1362,16 +1362,16 @@ rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
* Returns: %TRUE if the required number of consecutive packets was found.
*/
gboolean
rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
ts_rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
{
gboolean ret = TRUE;
RTPJitterBufferItem *last_item = NULL, *item;
gint i;
if (rtp_jitter_buffer_num_packets (jbuf) < (guint) num_packet)
if (ts_rtp_jitter_buffer_num_packets (jbuf) < (guint) num_packet)
return FALSE;
item = rtp_jitter_buffer_peek (jbuf);
item = ts_rtp_jitter_buffer_peek (jbuf);
for (i = 0; i < num_packet; i++) {
if (G_LIKELY (last_item)) {
guint16 expected_seqnum = last_item->seqnum + 1;
@ -1390,14 +1390,14 @@ rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
}
gboolean
rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf)
ts_rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf)
{
return rtp_jitter_buffer_get_seqnum_diff (jbuf) >= 32765 &&
rtp_jitter_buffer_num_packets (jbuf) > 10000;
return ts_rtp_jitter_buffer_get_seqnum_diff (jbuf) >= 32765 &&
ts_rtp_jitter_buffer_num_packets (jbuf) > 10000;
}
void
rtp_jitter_buffer_find_earliest (RTPJitterBuffer * jbuf, GstClockTime * pts,
ts_rtp_jitter_buffer_find_earliest (TsRTPJitterBuffer * jbuf, GstClockTime * pts,
guint * seqnum)
{
GList *tmp;

View file

@ -23,16 +23,16 @@
#include <gst/gst.h>
#include <gst/rtp/gstrtcpbuffer.h>
typedef struct _RTPJitterBuffer RTPJitterBuffer;
typedef struct _RTPJitterBufferClass RTPJitterBufferClass;
typedef struct _RTPJitterBuffer TsRTPJitterBuffer;
typedef struct _RTPJitterBufferClass TsRTPJitterBufferClass;
typedef struct _RTPJitterBufferItem RTPJitterBufferItem;
#define RTP_TYPE_JITTER_BUFFER (rtp_jitter_buffer_get_type())
#define RTP_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,RTPJitterBuffer))
#define RTP_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,RTPJitterBufferClass))
#define RTP_TYPE_JITTER_BUFFER (ts_rtp_jitter_buffer_get_type())
#define RTP_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,TsRTPJitterBuffer))
#define RTP_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,TsRTPJitterBufferClass))
#define RTP_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_JITTER_BUFFER))
#define RTP_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_JITTER_BUFFER))
#define RTP_JITTER_BUFFER_CAST(src) ((RTPJitterBuffer *)(src))
#define RTP_JITTER_BUFFER_CAST(src) ((TsRTPJitterBuffer *)(src))
/**
* RTPJitterBufferMode:
@ -61,8 +61,8 @@ typedef enum {
RTP_JITTER_BUFFER_MODE_LAST
} RTPJitterBufferMode;
#define RTP_TYPE_JITTER_BUFFER_MODE (rtp_jitter_buffer_mode_get_type())
GType rtp_jitter_buffer_mode_get_type (void);
#define RTP_TYPE_JITTER_BUFFER_MODE (ts_rtp_jitter_buffer_mode_get_type())
GType ts_rtp_jitter_buffer_mode_get_type (void);
#define RTP_JITTER_BUFFER_MAX_WINDOW 512
/**
@ -144,58 +144,58 @@ struct _RTPJitterBufferItem {
guint rtptime;
};
GType rtp_jitter_buffer_get_type (void);
GType ts_rtp_jitter_buffer_get_type (void);
/* managing lifetime */
RTPJitterBuffer* rtp_jitter_buffer_new (void);
TsRTPJitterBuffer* ts_rtp_jitter_buffer_new (void);
RTPJitterBufferMode rtp_jitter_buffer_get_mode (RTPJitterBuffer *jbuf);
void rtp_jitter_buffer_set_mode (RTPJitterBuffer *jbuf, RTPJitterBufferMode mode);
RTPJitterBufferMode ts_rtp_jitter_buffer_get_mode (TsRTPJitterBuffer *jbuf);
void ts_rtp_jitter_buffer_set_mode (TsRTPJitterBuffer *jbuf, RTPJitterBufferMode mode);
GstClockTime rtp_jitter_buffer_get_delay (RTPJitterBuffer *jbuf);
void rtp_jitter_buffer_set_delay (RTPJitterBuffer *jbuf, GstClockTime delay);
GstClockTime ts_rtp_jitter_buffer_get_delay (RTPJitterBuffer *jbuf);
void ts_rtp_jitter_buffer_set_delay (TsRTPJitterBuffer *jbuf, GstClockTime delay);
void rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer *jbuf, guint32 clock_rate);
guint32 rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf);
void ts_rtp_jitter_buffer_set_clock_rate (TsRTPJitterBuffer *jbuf, guint32 clock_rate);
guint32 ts_rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf);
void rtp_jitter_buffer_set_media_clock (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset);
void rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer *jbuf, GstClock * clock);
void ts_rtp_jitter_buffer_set_media_clock (TsRTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset);
void ts_rtp_jitter_buffer_set_pipeline_clock (TsRTPJitterBuffer *jbuf, GstClock * clock);
gboolean rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer *jbuf);
void rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer *jbuf, gboolean rfc7273_sync);
gboolean ts_rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer *jbuf);
void ts_rtp_jitter_buffer_set_rfc7273_sync (TsRTPJitterBuffer *jbuf, gboolean rfc7273_sync);
void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf);
void ts_rtp_jitter_buffer_reset_skew (TsRTPJitterBuffer *jbuf);
gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,
RTPJitterBufferItem *item,
gboolean *head, gint *percent);
gboolean ts_rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,
RTPJitterBufferItem *item,
gboolean *head, gint *percent);
void rtp_jitter_buffer_disable_buffering (RTPJitterBuffer *jbuf, gboolean disabled);
void ts_rtp_jitter_buffer_disable_buffering (TsRTPJitterBuffer *jbuf, gboolean disabled);
RTPJitterBufferItem * rtp_jitter_buffer_peek (RTPJitterBuffer *jbuf);
RTPJitterBufferItem * rtp_jitter_buffer_pop (RTPJitterBuffer *jbuf, gint *percent);
RTPJitterBufferItem * ts_rtp_jitter_buffer_peek (TsRTPJitterBuffer *jbuf);
RTPJitterBufferItem * ts_rtp_jitter_buffer_pop (TsRTPJitterBuffer *jbuf, gint *percent);
void rtp_jitter_buffer_flush (RTPJitterBuffer *jbuf,
GFunc free_func, gpointer user_data);
void ts_rtp_jitter_buffer_flush (TsRTPJitterBuffer *jbuf,
GFunc free_func, gpointer user_data);
gboolean rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf);
void rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering);
gint rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf);
gboolean ts_rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf);
void ts_rtp_jitter_buffer_set_buffering (TsRTPJitterBuffer * jbuf, gboolean buffering);
gint ts_rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf);
guint rtp_jitter_buffer_num_packets (RTPJitterBuffer *jbuf);
guint32 rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer *jbuf);
guint ts_rtp_jitter_buffer_num_packets (RTPJitterBuffer *jbuf);
guint32 ts_rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer *jbuf);
void rtp_jitter_buffer_get_sync (RTPJitterBuffer *jbuf, guint64 *rtptime,
guint64 *timestamp, guint32 *clock_rate,
guint64 *last_rtptime);
void ts_rtp_jitter_buffer_get_sync (TsRTPJitterBuffer *jbuf, guint64 *rtptime,
guint64 *timestamp, guint32 *clock_rate,
guint64 *last_rtptime);
GstClockTime rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, gboolean estimated_dts,
guint32 rtptime, GstClockTime base_time, gint gap,
gboolean is_rtx);
GstClockTime ts_rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, gboolean estimated_dts,
guint32 rtptime, GstClockTime base_time, gint gap,
gboolean is_rtx);
gboolean rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet);
gboolean ts_rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet);
gboolean rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf);
void rtp_jitter_buffer_find_earliest (RTPJitterBuffer * jbuf, GstClockTime *pts, guint * seqnum);
gboolean ts_rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf);
void ts_rtp_jitter_buffer_find_earliest (TsRTPJitterBuffer * jbuf, GstClockTime *pts, guint * seqnum);
#endif /* __RTP_JITTER_BUFFER_H__ */

View file

@ -22,7 +22,7 @@
#include "rtpstats.h"
void
gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
ts_gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
{
ctx->clock_rate = clock_rate;
ctx->probed = FALSE;
@ -31,7 +31,7 @@ gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
}
guint32
gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
ts_gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
guint32 ts)
{
guint64 new_ts, diff_ts;
@ -80,13 +80,13 @@ done:
}
guint32
gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
ts_gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
{
return ctx->avg_packet_rate;
}
guint32
gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
ts_gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
{
if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == G_MAXUINT32) {
return RTP_DEF_DROPOUT;
@ -96,7 +96,7 @@ gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
}
guint32
gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
ts_gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
gint32 time_ms)
{
if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == G_MAXUINT32) {
@ -113,9 +113,9 @@ gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
* Initialize @stats with its default values.
*/
void
rtp_stats_init_defaults (RTPSessionStats * stats)
ts_rtp_stats_init_defaults (RTPSessionStats * stats)
{
rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
ts_rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
stats->min_interval = RTP_STATS_MIN_INTERVAL;
stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
stats->nacks_dropped = 0;
@ -136,7 +136,7 @@ rtp_stats_init_defaults (RTPSessionStats * stats)
* defaults.
*/
void
rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
ts_rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
gdouble rtcp_bw, guint rs, guint rr)
{
GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %f, RS %u, RR %u", rtp_bw,
@ -217,7 +217,7 @@ rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
* Returns: the RTCP interval.
*/
GstClockTime
rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
ts_rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
GstRTPProfile profile, gboolean ptp, gboolean first)
{
gdouble members, senders, n;
@ -296,7 +296,7 @@ rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
* Returns: the new RTCP interval.
*/
GstClockTime
rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
ts_rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
GstClockTime interval)
{
gdouble temp;
@ -323,7 +323,7 @@ rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
* Returns: the BYE interval.
*/
GstClockTime
rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
ts_rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
{
gdouble members;
gdouble avg_rtcp_size, rtcp_bw;
@ -377,7 +377,7 @@ rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
* Returns: total RTP packets lost.
*/
gint64
rtp_stats_get_packets_lost (const RTPSourceStats * stats)
ts_rtp_stats_get_packets_lost (const RTPSourceStats * stats)
{
gint64 lost;
guint64 extended_max, expected;
@ -390,41 +390,7 @@ rtp_stats_get_packets_lost (const RTPSourceStats * stats)
}
void
rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
ts_rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
{
stats->min_interval = min_interval;
}
gboolean
__g_socket_address_equal (GSocketAddress * a, GSocketAddress * b)
{
GInetSocketAddress *ia, *ib;
GInetAddress *iaa, *iab;
ia = G_INET_SOCKET_ADDRESS (a);
ib = G_INET_SOCKET_ADDRESS (b);
if (g_inet_socket_address_get_port (ia) !=
g_inet_socket_address_get_port (ib))
return FALSE;
iaa = g_inet_socket_address_get_address (ia);
iab = g_inet_socket_address_get_address (ib);
return g_inet_address_equal (iaa, iab);
}
gchar *
__g_socket_address_to_string (GSocketAddress * addr)
{
GInetSocketAddress *ia;
gchar *ret, *tmp;
ia = G_INET_SOCKET_ADDRESS (addr);
tmp = g_inet_address_to_string (g_inet_socket_address_get_address (ia));
ret = g_strdup_printf ("%s:%u", tmp, g_inet_socket_address_get_port (ia));
g_free (tmp);
return ret;
}

View file

@ -216,11 +216,11 @@ typedef struct {
guint32 avg_packet_rate;
} RTPPacketRateCtx;
void gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate);
guint32 gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx *ctx, guint16 seqnum, guint32 ts);
guint32 gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx *ctx);
guint32 gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx *ctx, gint32 time_ms);
guint32 gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx *ctx, gint32 time_ms);
void ts_gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate);
guint32 ts_gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx *ctx, guint16 seqnum, guint32 ts);
guint32 ts_gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx *ctx);
guint32 ts_gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx *ctx, gint32 time_ms);
guint32 ts_gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx *ctx, gint32 time_ms);
/**
* RTPSessionStats:
@ -245,23 +245,19 @@ typedef struct {
guint nacks_received;
} RTPSessionStats;
void rtp_stats_init_defaults (RTPSessionStats *stats);
void ts_rtp_stats_init_defaults (RTPSessionStats *stats);
void rtp_stats_set_bandwidths (RTPSessionStats *stats,
guint rtp_bw,
gdouble rtcp_bw,
guint rs, guint rr);
void ts_rtp_stats_set_bandwidths (RTPSessionStats *stats,
guint rtp_bw,
gdouble rtcp_bw,
guint rs, guint rr);
GstClockTime rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gboolean sender, GstRTPProfile profile, gboolean ptp, gboolean first);
GstClockTime rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval);
GstClockTime rtp_stats_calculate_bye_interval (RTPSessionStats *stats);
gint64 rtp_stats_get_packets_lost (const RTPSourceStats *stats);
GstClockTime ts_rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gboolean sender, GstRTPProfile profile, gboolean ptp, gboolean first);
GstClockTime ts_rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval);
GstClockTime ts_rtp_stats_calculate_bye_interval (RTPSessionStats *stats);
gint64 ts_rtp_stats_get_packets_lost (const RTPSourceStats *stats);
void rtp_stats_set_min_interval (RTPSessionStats *stats,
gdouble min_interval);
gboolean __g_socket_address_equal (GSocketAddress *a, GSocketAddress *b);
gchar * __g_socket_address_to_string (GSocketAddress * addr);
void ts_rtp_stats_set_min_interval (RTPSessionStats *stats,
gdouble min_interval);
#endif /* __RTP_STATS_H__ */

View file

@ -16,29 +16,30 @@
#[macro_use]
pub mod runtime;
pub mod socket;
mod tcpclientsrc;
mod udpsink;
mod udpsrc;
mod appsrc;
mod audiotestsrc;
pub mod dataqueue;
mod inputselector;
mod jitterbuffer;
mod proxy;
mod queue;
pub mod socket;
mod tcpclientsrc;
mod udpsink;
mod udpsrc;
use gst::glib;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
udpsrc::register(plugin)?;
udpsink::register(plugin)?;
tcpclientsrc::register(plugin)?;
queue::register(plugin)?;
proxy::register(plugin)?;
appsrc::register(plugin)?;
jitterbuffer::register(plugin)?;
audiotestsrc::register(plugin)?;
inputselector::register(plugin)?;
jitterbuffer::register(plugin)?;
proxy::register(plugin)?;
queue::register(plugin)?;
tcpclientsrc::register(plugin)?;
udpsink::register(plugin)?;
udpsrc::register(plugin)?;
Ok(())
}

File diff suppressed because it is too large Load diff

View file

@ -375,6 +375,8 @@ impl TaskImpl for UdpSrcTask {
)
})?;
drop(settings);
self.socket = Some(
Socket::try_new(
self.element.clone().upcast(),

View file

@ -336,13 +336,13 @@ fn eos() {
.name("src-eos")
.property("caps", &caps)
.property("do-timestamp", true)
.property("context", &CONTEXT)
.property("context", CONTEXT)
.build()
.unwrap();
let queue = gst::ElementFactory::make("ts-queue")
.name("queue-eos")
.property("context", &CONTEXT)
.property("context", CONTEXT)
.build()
.unwrap();
@ -636,7 +636,7 @@ fn socket_play_null_play() {
let sink = gst::ElementFactory::make("ts-udpsink")
.name(format!("sink-{}", TEST).as_str())
.property("socket", &socket)
.property("context", &TEST)
.property("context", TEST)
.property("context-wait", 20u32)
.build()
.unwrap();

View file

@ -100,6 +100,6 @@ fn test_chain() {
assert!(buf == [42, 43, 44, 45, 0]);
});
let buf = gst::Buffer::from_slice(&[42, 43, 44, 45]);
let buf = gst::Buffer::from_slice([42, 43, 44, 45]);
assert!(h.push(buf) == Ok(gst::FlowSuccess::Ok));
}

View file

@ -1,11 +1,16 @@
project('gst-plugins-rs',
'rust',
'c',
version: '0.9.0-alpha.1',
version: '0.9.13',
meson_version : '>= 0.60')
python = import('python').find_installation()
# dependencies.py needs a toml parsing module
python = import('python').find_installation(modules: ['tomllib'], required: false)
if not python.found()
python = import('python').find_installation(modules: ['tomli'])
endif
fs = import('fs')
host_system = host_machine.system()
if get_option('debug')
target = 'debug'
@ -16,16 +21,17 @@ endif
cargo = find_program('cargo', version:'>=1.40')
cargo_wrapper = find_program('cargo_wrapper.py')
cargo_c = find_program('cargo-cbuild', version:'>=0.9.3', required: false)
rustc = find_program('rustc', version:'>=1.52')
rustc = meson.get_compiler('rust')
if not cargo_c.found()
error('cargo-c missing, install it with: \'cargo install cargo-c\'')
endif
system = build_machine.system()
ext_exe = ''
system = host_machine.system()
exe_suffix = ''
if system == 'windows'
ext_exe = 'exe'
exe_suffix = '.exe'
ext_dynamic = 'dll'
ext_static = 'lib'
elif system == 'darwin'
@ -36,135 +42,18 @@ else
ext_static = 'a'
endif
# workspace name -> lib name
# kept in the same order as the `members` list in Cargo.toml
plugins = {
'gst-plugin-audiofx': 'libgstrsaudiofx',
'gst-plugin-claxon': 'libgstclaxon',
# csound has an external dependency, see below
'gst-plugin-lewton': 'libgstlewton',
'gst-plugin-spotify': 'libgstspotify',
'gst-plugin-file': 'libgstrsfile',
# sodium has an external dependency, see below
'gst-plugin-threadshare': 'libgstthreadshare',
'gst-plugin-fmp4': 'libgstfmp4',
'gst-plugin-aws': 'libgstaws',
'gst-plugin-hlssink3': 'libgsthlssink3',
'gst-plugin-ndi': 'libgstndi',
'gst-plugin-onvif': 'libgstrsonvif',
'gst-plugin-raptorq': 'libgstraptorq',
'gst-plugin-reqwest': 'libgstreqwest',
'gst-plugin-rtp': 'libgstrsrtp',
'gst-plugin-webrtchttp': 'libgstwebrtchttp',
'gst-plugin-webrtc': 'libgstrswebrtc',
'gst-plugin-textahead': 'libgsttextahead',
'gst-plugin-json': 'libgstjson',
'gst-plugin-regex': 'libgstregex',
'gst-plugin-textwrap': 'libgsttextwrap',
'gst-plugin-fallbackswitch': 'libgstfallbackswitch',
'gst-plugin-togglerecord': 'libgsttogglerecord',
'gst-plugin-tracers': 'libgstrstracers',
'gst-plugin-uriplaylistbin': 'libgsturiplaylistbin',
'gst-plugin-cdg': 'libgstcdg',
# closedcaption has an external dependency, see below
# dav1d has an external dependency, see below
'gst-plugin-ffv1': 'libgstffv1',
'gst-plugin-flavors': 'libgstrsflv',
'gst-plugin-gif': 'libgstgif',
# gtk4 has an external dependency, see below
'gst-plugin-hsv': 'libgsthsv',
'gst-plugin-png': 'libgstrspng',
'gst-plugin-rav1e': 'libgstrav1e',
# videofx has an external dependency, see below
# FIXME: libwebp-sys2 will build its bundled version on msvc and apple platforms
# https://github.com/qnighy/libwebp-sys2-rs/issues/4
'gst-plugin-webp': 'libgstrswebp',
}
# Extra env to pass to cargo
extra_env = {}
if dependency('cairo-gobject', required : get_option('videofx')).found()
plugins += {'gst-plugin-videofx': 'libgstrsvideofx',}
endif
if dependency('pangocairo', required : get_option('closedcaption')).found()
plugins += {'gst-plugin-closedcaption' : 'libgstrsclosedcaption',}
endif
if dependency('dav1d', version : '>= 1.0.0', required : get_option('dav1d')).found()
plugins += {'gst-plugin-dav1d' : 'libgstdav1d'}
endif
sodium = get_option ('sodium')
if sodium == 'system'
dependency('libsodium')
plugins += {'gst-plugin-sodium': 'libgstsodium'}
extra_env += {'SODIUM_USE_PKG_CONFIG': '1'}
elif sodium == 'built-in'
plugins += {'gst-plugin-sodium': 'libgstsodium'}
endif
cc = meson.get_compiler('c')
csound_option = get_option('csound')
# try first to find csound using pkg-config
csound_dep = dependency('', required: false)
if not csound_dep.found() and not csound_option.disabled()
# if csound isn't distributed with pkg-config then user needs to define CSOUND_LIB_DIR with its location
res = run_command(python, '-c', 'import os; print(os.environ["CSOUND_LIB_DIR"])', check: false)
if res.returncode() == 0
csound_libdir = res.stdout().strip()
csound_dep = cc.find_library('csound64', dirs: csound_libdir, required: false)
if csound_dep.found()
extra_env += {'CSOUND_LIB_DIR': csound_libdir}
endif
endif
endif
if csound_dep.found()
plugins += {'gst-plugin-csound' : 'libgstcsound'}
elif csound_option.enabled()
error('csound option is enabled, but csound64 library could not be found and CSOUND_LIB_DIR was not set')
else
message('csound not found, disabling its plugin')
endif
if dependency('gtk4', required : get_option('gtk4')).found()
plugins += {'gst-plugin-gtk4' : 'libgstgtk4',}
endif
output = []
extensions = []
# Add the plugin file as output
if get_option('default_library') == 'shared' or get_option('default_library') == 'both'
extensions += [ext_dynamic]
foreach p, lib : plugins
output += [lib + '.' + ext_dynamic]
endforeach
endif
static_build = get_option('default_library') == 'static'
if static_build or get_option('default_library') == 'both'
extensions += [ext_static]
foreach p, lib : plugins
output += [lib + '.' + ext_static]
endforeach
endif
# Used to not lookup the same dependency multiple times which clutters logs
deps_cache = {}
# Need to depends on all gstreamer-rs deps to ensure they are built
# before gstreamer-rs when building with gst-build.
# Custom targets can't depend on dependency() objects so we have to depend
# on the library variable from the subproject instead.
gst_req = '>= 1.18.0'
glib_req = '>=2.62'
gst_req = '>=1.20.0'
depends = []
deps = [
@ -173,19 +62,36 @@ deps = [
['gstreamer-app-1.0', 'gst-plugins-base', 'app_dep', 'gstapp'],
['gstreamer-audio-1.0', 'gst-plugins-base', 'audio_dep', 'gstaudio'],
['gstreamer-base-1.0', 'gstreamer', 'gst_base_dep', 'gst_base'],
['gstreamer-check-1.0', 'gstreamer', 'gst_check_dep', 'gst_check'],
['gstreamer-net-1.0', 'gstreamer', 'gst_net_dep', 'gst_net'],
['gstreamer-rtp-1.0', 'gst-plugins-base', 'rtp_dep', 'gst_rtp'],
['gstreamer-video-1.0', 'gst-plugins-base', 'video_dep', 'gstvideo'],
['gstreamer-sdp-1.0', 'gst-plugins-base', 'sdp_dep', 'gstsdp'],
['gstreamer-webrtc-1.0', 'gst-plugins-bad', 'gstwebrtc_dep', 'gstwebrtc'],
]
# Used to not lookup the same dependency multiple times which clutters logs
deps_cache = {}
if get_option('threadshare').allowed() \
or get_option('onvif').allowed() \
or get_option('raptorq').allowed() \
or get_option('rtp').allowed() \
or get_option('webrtc').allowed()
deps += [['gstreamer-rtp-1.0', 'gst-plugins-base', 'rtp_dep', 'gst_rtp']]
endif
if get_option('webrtc').allowed() \
or get_option('webrtchttp').allowed()
deps += [['gstreamer-webrtc-1.0', 'gst-plugins-bad', 'gstwebrtc_dep', 'gstwebrtc']]
deps += [['gstreamer-sdp-1.0', 'gst-plugins-base', 'sdp_dep', 'gstsdp']]
endif
if get_option('tests').allowed()
deps += [['gstreamer-check-1.0', 'gstreamer', 'gst_check_dep', 'gst_check']]
endif
if get_option('gtk4').allowed()
deps += [['gstreamer-gl-1.0', 'gst-plugins-base', 'gst_gl_dep', 'gstgl']]
endif
if get_option('threadshare').allowed()
deps += [['gstreamer-net-1.0', 'gstreamer', 'gst_net_dep', 'gst_net']]
endif
glib_dep = dependency('glib-2.0', version: glib_req)
deps_cache += {'glib-2.0': glib_dep}
foreach d: deps
dep = dependency(d[0], version : gst_req,
dep = dependency(d[0], version: gst_req,
fallback : [d[1], d[2]])
set_variable(d[2], dep)
deps_cache += {d[0]: dep}
@ -195,55 +101,393 @@ foreach d: deps
endif
endforeach
include = ','.join(plugins.keys())
# kept in the same order as the `members` list in Cargo.toml
plugins = {
'audiofx': {
'library': 'libgstrsaudiofx',
'examples': ['hrtfrender'],
},
'claxon': {'library': 'libgstclaxon'},
# csound has a non-trivial external dependency, see below
'lewton': {'library': 'libgstlewton'},
'spotify': {'library': 'libgstspotify'},
# serialize extra_env
extra_env_list = []
foreach key, value : extra_env
extra_env_list += key + ':' + value
'file': {'library': 'libgstrsfile'},
# sodium can have an external dependency, see below
'threadshare': {
'library': 'libgstthreadshare',
'examples': [
'ts-benchmark',
'udpsrc-benchmark-sender',
'tcpclientsrc-benchmark-sender',
'ts-standalone',
],
},
'mp4': {'library': 'libgstmp4'},
'fmp4': {
'library': 'libgstfmp4',
'examples': [
'dash_vod',
'hls_live',
'hls_vod',
],
},
'aws': {
'library': 'libgstaws',
'extra-deps': {'openssl': '>=1.1'},
},
'hlssink3': {'library': 'libgsthlssink3'},
'ndi': {'library': 'libgstndi'},
'onvif': {
'library': 'libgstrsonvif',
'extra-deps': {'pangocairo': ''},
},
'raptorq': {'library': 'libgstraptorq'},
'reqwest': {'library': 'libgstreqwest'},
'rtp': {'library': 'libgstrsrtp'},
'webrtchttp': {'library': 'libgstwebrtchttp'},
'webrtc': {
'library': 'libgstrswebrtc',
'examples': ['webrtcsink-stats-server'],
},
'textahead': {'library': 'libgsttextahead'},
'json': {'library': 'libgstjson'},
'regex': {'library': 'libgstregex'},
'textwrap': {'library': 'libgsttextwrap'},
'tracers': {'library': 'libgstrstracers'},
'uriplaylistbin': {
'library': 'libgsturiplaylistbin',
'examples': ['playlist'],
'features': ['clap'],
},
'cdg': {'library': 'libgstcdg'},
'closedcaption': {
'library': 'libgstrsclosedcaption',
'extra-deps': {
'pango': '',
'pangocairo': '',
'cairo-gobject': '',
}
},
'dav1d': {
'library': 'libgstdav1d',
'extra-deps': {'dav1d': '>=1.0'},
},
'ffv1': {'library': 'libgstffv1'},
'flavors': {'library': 'libgstrsflv'},
'gif': {
'library': 'libgstgif',
'examples': ['testvideosrc2gif'],
},
# gtk4 is added below
'hsv': {'library': 'libgsthsv'},
'png': {
'library': 'libgstrspng',
'examples': ['pngenc'],
},
'rav1e': {'library': 'libgstrav1e'},
'videofx': {
'library': 'libgstrsvideofx',
'extra-deps': {'cairo-gobject': ''},
},
}
if get_option('examples').allowed()
plugins += {
'fallbackswitch': {
'library': 'libgstfallbackswitch',
'examples': ['gtk-fallbackswitch'],
'features': ['gtk', 'gio', 'gst-plugin-gtk4'],
},
'livesync': {
'library': 'libgstlivesync',
'examples': ['gtk-livesync'],
'features': ['gtk', 'gio', 'gst-plugin-gtk4'],
},
'togglerecord': {
'library': 'libgsttogglerecord',
'examples': ['gtk-recording'],
'features': ['gtk', 'gio', 'gst-plugin-gtk4'],
},
}
else
plugins += {
'fallbackswitch': { 'library': 'libgstfallbackswitch'},
'livesync': { 'library': 'libgstlivesync'},
'togglerecord': { 'library': 'libgsttogglerecord'},
}
endif
# Won't build on platforms where it bundles the sources because of:
# https://github.com/qnighy/libwebp-sys2-rs/issues/12
# the fix is:
# https://github.com/qnighy/libwebp-sys2-rs/pull/13
if host_system not in ['windows', 'darwin']
# FIXME: libwebp-sys2 will build its bundled version on msvc and apple platforms
# https://github.com/qnighy/libwebp-sys2-rs/issues/4
plugins += {'webp': {
'library': 'libgstrswebp',
'extra-deps': {'libwebpdemux': ''},
}}
endif
sodium_opt = get_option('sodium')
if sodium_opt.allowed()
sodium_plugin = {'sodium': {
'library': 'libgstsodium',
'examples': ['generate-keys', 'encrypt-example', 'decrypt-example'],
'features': ['serde', 'serde_json', 'clap'],
}}
if get_option('sodium-source') == 'system'
sodium_dep = dependency('libsodium', required: sodium_opt.enabled())
extra_env += {'SODIUM_USE_PKG_CONFIG': '1'}
if sodium_dep.found()
plugins += sodium_plugin
endif
else
plugins += sodium_plugin
endif
endif
cc = meson.get_compiler('c')
csound_option = get_option('csound')
if csound_option.allowed()
# if csound isn't distributed with pkg-config then user needs to define CSOUND_LIB_DIR with its location
res = run_command(python, '-c', 'import os; print(os.environ["CSOUND_LIB_DIR"])', check: false)
if res.returncode() == 0
csound_libdir = res.stdout().strip()
csound_dep = cc.find_library('csound64', dirs: csound_libdir, required: false)
if csound_dep.found()
plugins += {'csound': {
'library': 'libgstcsound',
'examples': ['csound-effect'],
}}
extra_env += {'CSOUND_LIB_DIR': csound_libdir}
elif csound_option.enabled()
error('csound option is enabled, but csound64 library could not be found and CSOUND_LIB_DIR was not set')
endif
endif
endif
if get_option('gtk4').allowed()
gtk4_features = []
gl_winsys = gst_gl_dep.get_variable('gl_winsys').split()
gl_platforms = gst_gl_dep.get_variable('gl_platforms').split()
if 'wayland' in gl_winsys
gtk4_features += 'wayland'
endif
if 'x11' in gl_winsys
if 'egl' in gl_platforms
gtk4_features += 'x11egl'
endif
if 'glx' in gl_platforms
gtk4_features += 'x11glx'
endif
endif
plugins += {
'gtk4': {
'library': 'libgstgtk4',
'examples': ['gtksink'],
'extra-deps': {'gtk4': '>=4.6'},
'features': gtk4_features,
},
}
endif
# Process plugins list
default_library = get_option('default_library')
library_suffixes = []
if default_library in ['shared', 'both']
library_suffixes += [ext_dynamic]
endif
if default_library in ['static', 'both']
library_suffixes += [ext_static]
endif
# cargo packages (plugins) to build
packages = []
# cargo features
features = []
# examples to build
examples = []
# Add the plugin library files as output
output = []
if get_option('gtk4').allowed()
if glib_dep.version().version_compare('>=2.74')
features += ['glib/v2_74', 'gio/v2_74']
elif glib_dep.version().version_compare('>=2.72')
features += ['glib/v2_72', 'gio/v2_72']
elif glib_dep.version().version_compare('>=2.70')
features += ['glib/v2_70', 'gio/v2_70']
elif glib_dep.version().version_compare('>=2.68')
features += ['glib/v2_68', 'gio/v2_68']
elif glib_dep.version().version_compare('>=2.66')
features += ['glib/v2_66', 'gio/v2_66']
elif glib_dep.version().version_compare('>=2.64')
features += ['glib/v2_64', 'gio/v2_64']
elif glib_dep.version().version_compare('>=2.62')
features += ['glib/v2_62', 'gio/v2_62']
elif glib_dep.version().version_compare('>=2.60')
features += ['glib/v2_60', 'gio/v2_60']
elif glib_dep.version().version_compare('>=2.58')
features += ['glib/v2_58', 'gio/v2_58']
endif
endif
if get_option('rav1e').allowed()
nasm = find_program('nasm', required: false)
if nasm.found()
features += 'gst-plugin-rav1e/asm'
extra_env += {'NASM': nasm.full_path()}
endif
endif
if get_option('default_library') == 'static'
extra_env += {
# Tell the pkg-config crate to think of all libraries as static
'PKG_CONFIG_ALL_STATIC': '1',
# Tell the system-deps crate to process linker flag for static deps
'SYSTEM_DEPS_LINK': 'static'
}
endif
foreach plugin_name, details: plugins
plugin_opt = get_option(plugin_name)
if plugin_opt.allowed()
plugin_deps_found = true
foreach dep_name, dep_ver: details.get('extra-deps', {})
if dep_ver != ''
dep = dependency(dep_name, version: dep_ver, required: plugin_opt)
else
dep = dependency(dep_name, required: plugin_opt)
endif
deps_cache += {dep_name: dep}
if not dep.found()
plugin_deps_found = false
break
endif
endforeach
plugin_features = details.get('features', [])
if plugin_deps_found
# Validate gst-plugin features
foreach feature: plugin_features
if feature.startswith('gst-plugin') and not packages.contains(feature)
plugin_deps_found = false
break
endif
endforeach
endif
if plugin_deps_found
packages += f'gst-plugin-@plugin_name@'
features += plugin_features
extra_features = run_command('dependencies.py', meson.current_source_dir(), plugin_name,
'--feature', '--gst-version', gst_dep.version(), capture: true, check: true).stdout().strip()
if extra_features != ''
features += extra_features.split(',')
endif
lib = details.get('library')
# No 'lib' suffix with MSVC
if cc.get_argument_syntax() == 'msvc'
lib = lib.substring(3)
endif
if default_library in ['shared', 'both']
output += [lib + '.' + ext_dynamic]
endif
if default_library in ['static', 'both']
output += [lib + '.' + ext_static]
endif
endif
endif
endforeach
extra_env_str = ','.join(extra_env_list)
feature_args = []
if features.length() > 0
feature_args += ['--features', features]
endif
plugins_install_dir = get_option('libdir') / 'gstreamer-1.0'
pkgconfig_install_dir = get_option('libdir') / 'pkgconfig'
extra_args = []
if get_option('doc').disabled()
disable_doc = ['--disable-doc']
else
disable_doc = []
extra_args += ['--disable-doc']
endif
rs_plugins = custom_target('gst-plugins-rs',
build_by_default: true,
output: output,
console: true,
install: true,
install_dir: plugins_install_dir,
depends: depends,
depfile: 'gst-plugins-rs.dep',
command: [cargo_wrapper,
'build',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
include,
extra_env_str,
get_option('prefix'),
get_option('libdir'),
'--depfile', '@DEPFILE@',
'--exts', extensions,
] + disable_doc)
# 'pkgconfig' is the entry in the machine file, if specified
pkg_config = find_program('pkgconfig', 'pkg-config')
if pkg_config.found()
extra_env += {'PKG_CONFIG': pkg_config.full_path()}
endif
plugins = rs_plugins.to_list()
pkg_config_path = get_option('pkg_config_path')
if pkg_config_path.length() > 0
pathsep = ':'
if host_system == 'windows'
pathsep = ';'
endif
extra_env += {'PKG_CONFIG_PATH': pathsep.join(pkg_config_path)}
endif
# get cmdline for rust
extra_env += {'RUSTC': ' '.join(rustc.cmd_array())}
plugins = []
if output.length() > 0
rs_plugins = custom_target('gst-plugins-rs',
build_by_default: true,
output: output,
console: true,
install: true,
install_dir: plugins_install_dir,
depends: depends,
depfile: 'gst-plugins-rs.dep',
env: extra_env,
command: [cargo_wrapper,
'build',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
get_option('prefix'),
get_option('libdir'),
'--packages', packages,
'--depfile', '@DEPFILE@',
'--lib-suffixes', library_suffixes,
] + feature_args + extra_args)
plugins = rs_plugins.to_list()
endif
# This is used by GStreamer to static link Rust plugins into gst-full
gst_plugins = []
pc_files = []
plugin_names = []
foreach plugin : plugins
# skip the 'lib' prefix and extension from plugin path
plugin_name = fs.stem(plugin.full_path()).substring(3)
plugin_name = fs.stem(plugin.full_path())
# skip the 'lib' prefix from plugin path when not building with MSVC
if cc.get_argument_syntax() != 'msvc'
plugin_name = plugin_name.substring(3)
endif
option_name = plugin_name.substring(3)
if option_name.startswith('rs')
option_name = option_name.substring(2)
endif
if option_name == 'flv'
option_name = 'flavors'
endif
if not get_option(option_name).allowed()
continue
endif
# Extract plugin dependencies from their Cargo.toml file
plugin_deps = []
@ -252,9 +496,9 @@ foreach plugin : plugins
check: true)
foreach dep_name : p.stdout().strip().split(',')
dep_name_version = dep_name.split('|')
dep_name = dep_name_version.get(0)
dep_name = dep_name_version.get(0).strip()
if dep_name_version.length() > 1
extras = {'version': dep_name_version.get(1)}
extras = {'version': dep_name_version.get(1).strip()}
else
extras = {}
endif
@ -274,17 +518,17 @@ foreach plugin : plugins
)
meson.override_dependency(plugin_name, dep)
if static_build and plugin_name in ['gstcsound', 'gstthreadshare']
if default_library == 'static' and plugin_name in ['gstcsound', 'gstthreadshare', 'gstgtk4']
warning('Static plugin @0@ is known to fail. It will not be included in libgstreamer-full.'.format(plugin_name))
else
gst_plugins += dep
endif
pc_files += [plugin_name + '.pc']
if plugin_name.startswith('gst')
plugin_names += [plugin_name.substring(3)]
else
plugin_names += [plugin_name]
pc_files += [plugin_name + '.pc']
if plugin_name.startswith('gst')
plugin_names += [plugin_name.substring(3)]
else
plugin_names += [plugin_name]
endif
endif
endforeach
@ -293,50 +537,81 @@ subdir('docs')
# We don't need to pass a command as we depends on the target above
# but it is currently mandatory ( https://github.com/mesonbuild/meson/issues/8059 )
# so use python as it's guaranteed to be present on any setup
custom_target('gst-plugins-rs-pc-files',
build_by_default: true,
output: pc_files,
console: true,
install: true,
install_dir: pkgconfig_install_dir,
depends: rs_plugins,
command: [python, '-c', '""'])
if pc_files.length() > 0
custom_target('gst-plugins-rs-pc-files',
build_by_default: true,
output: pc_files,
console: true,
install: true,
install_dir: pkgconfig_install_dir,
depends: rs_plugins,
command: [python, '-c', '""'])
endif
if get_option('webrtc').allowed()
custom_target('gst-webrtc-signalling-server',
build_by_default: true,
output: 'gst-webrtc-signalling-server' + exe_suffix,
console: true,
install: true,
install_dir: get_option('bindir'),
depfile: 'gst-webrtc-signalling-server.dep',
env: extra_env,
command: [cargo_wrapper,
'build',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
get_option('prefix'),
get_option('libdir'),
'--depfile', '@DEPFILE@',
'--bin', 'gst-webrtc-signalling-server',
'--exe-suffix', exe_suffix,
])
endif
custom_target('gst-webrtc-signalling-server',
build_by_default: true,
output: 'gst-webrtc-signalling-server',
console: true,
install: true,
install_dir: get_option('bindir'),
depfile: 'gst-webrtc-signalling-server.dep',
command: [cargo_wrapper,
'build',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
'',
'',
get_option('prefix'),
get_option('libdir'),
'--depfile', '@DEPFILE@',
'--exts', ext_exe,
'--bin', 'gst-webrtc-signalling-server'
])
if get_option('examples').allowed() and examples.length() > 0
outputs = []
foreach example: examples
outputs += [example + exe_suffix]
endforeach
custom_target('gst-plugins-rs-examples',
build_by_default: true,
output: outputs,
console: true,
install: false,
depfile: 'gst-plugins-rs-examples.dep',
env: extra_env,
command: [cargo_wrapper,
'build',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
get_option('prefix'),
get_option('libdir'),
'--depfile', '@DEPFILE@',
'--packages', packages,
'--examples', examples,
'--exe-suffix', exe_suffix,
] + feature_args)
endif
test('tests',
cargo_wrapper,
args: ['test',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
include,
extra_env_str,
get_option('prefix'),
get_option('libdir')],
timeout: 600)
if get_option('tests').allowed()
test('tests',
cargo_wrapper,
env: extra_env,
args: ['test',
meson.current_build_dir(),
meson.current_source_dir(),
meson.global_build_root(),
target,
get_option('prefix'),
get_option('libdir'),
'--packages', packages],
timeout: 600)
endif
summary({
'Plugins': plugin_names,

View file

@ -1,12 +1,66 @@
option('videofx', type : 'feature', value : 'auto', description : 'Build videofx plugin')
option('closedcaption', type : 'feature', value : 'auto', description : 'Build closedcaption plugin')
option('dav1d', type : 'feature', value : 'auto', description : 'Build dav1d plugin')
option('sodium', type : 'combo',
choices : ['system', 'built-in', 'disabled'], value : 'built-in',
description : 'Weither to use libsodium from the system or the built-in version from the sodiumoxide crate')
option('csound', type : 'feature', value : 'auto', description : 'Build csound plugin')
option('gtk4', type : 'feature', value : 'auto', description : 'Build GTK4 plugin')
# Same order as members in Cargo.toml
# audio
option('audiofx', type: 'feature', value: 'auto', description: 'Build audiofx plugin')
option('claxon', type: 'feature', value: 'auto', description: 'Build claxon plugin')
option('csound', type: 'feature', value: 'auto', description: 'Build csound plugin')
option('lewton', type: 'feature', value: 'auto', description: 'Build lewton plugin')
option('spotify', type: 'feature', value: 'auto', description: 'Build spotify plugin')
# generic
option('file', type: 'feature', value: 'auto', description: 'Build file plugin')
option('sodium', type: 'feature', value: 'auto', description: 'Build sodium plugin')
option('sodium-source', type: 'combo',
choices: ['system', 'built-in'], value: 'built-in',
description: 'Whether to use libsodium from the system or the built-in version from the sodiumoxide crate')
option('threadshare', type: 'feature', value: 'auto', description: 'Build threadshare plugin')
# mux
option('flavors', type: 'feature', value: 'auto', description: 'Build flavors plugin')
option('fmp4', type: 'feature', value: 'auto', description: 'Build fmp4 plugin')
option('mp4', type: 'feature', value: 'auto', description: 'Build mp4 plugin')
# net
option('aws', type: 'feature', value: 'auto', description: 'Build aws plugin')
option('hlssink3', type: 'feature', value: 'auto', description: 'Build hlssink3 plugin')
option('ndi', type: 'feature', value: 'auto', description: 'Build ndi plugin')
option('onvif', type: 'feature', value: 'auto', description: 'Build onvif plugin')
option('raptorq', type: 'feature', value: 'auto', description: 'Build raptorq plugin')
option('reqwest', type: 'feature', value: 'auto', description: 'Build reqwest plugin')
option('rtp', type: 'feature', value: 'auto', description: 'Build rtp plugin')
option('webrtc', type: 'feature', value: 'auto', description: 'Build webrtc plugin')
option('webrtchttp', type: 'feature', value: 'auto', description: 'Build webrtchttp plugin')
# text
option('textahead', type: 'feature', value: 'auto', description: 'Build textahead plugin')
option('json', type: 'feature', value: 'auto', description: 'Build json plugin')
option('regex', type: 'feature', value: 'auto', description: 'Build regex plugin')
option('textwrap', type: 'feature', value: 'auto', description: 'Build textwrap plugin')
# utils
option('fallbackswitch', type: 'feature', value: 'auto', description: 'Build fallbackswitch plugin')
option('livesync', type: 'feature', value: 'auto', description: 'Build livesync plugin')
option('togglerecord', type: 'feature', value: 'auto', description: 'Build togglerecord plugin')
option('tracers', type: 'feature', value: 'auto', description: 'Build tracers plugin')
option('uriplaylistbin', type: 'feature', value: 'auto', description: 'Build uriplaylistbin plugin')
# video
option('cdg', type: 'feature', value: 'auto', description: 'Build cdg plugin')
option('closedcaption', type: 'feature', value: 'auto', description: 'Build closedcaption plugin')
option('dav1d', type: 'feature', value: 'auto', description: 'Build dav1d plugin')
option('ffv1', type: 'feature', value: 'auto', description: 'Build ffv1 plugin')
option('gif', type: 'feature', value: 'auto', description: 'Build gif plugin')
option('gtk4', type: 'feature', value: 'auto', description: 'Build GTK4 plugin')
option('hsv', type: 'feature', value: 'auto', description: 'Build hsv plugin')
option('png', type: 'feature', value: 'auto', description: 'Build png plugin')
option('rav1e', type: 'feature', value: 'auto', description: 'Build rav1e plugin')
option('videofx', type: 'feature', value: 'auto', description: 'Build videofx plugin')
option('webp', type: 'feature', value: 'auto', description: 'Build webp plugin')
# Common options
option('doc', type : 'feature', value : 'auto', yield: true,
description: 'Enable documentation.')
option('doc', type: 'feature', value: 'auto', yield: true,
description: 'Enable documentation')
option('examples', type: 'feature', value: 'disabled', yield: true,
description: 'Build examples')
option('tests', type : 'feature', value : 'auto', yield : true,
description : 'Build and enable unit tests')

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-flavors"
version = "0.9.0-alpha.1"
version = "0.9.13"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
@ -9,9 +9,9 @@ rust-version = "1.63"
description = "GStreamer Rust FLV Plugin"
[dependencies]
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
num-rational = { version = "0.4", default-features = false, features = [] }
nom = "7"
flavors = { git = "https://github.com/rust-av/flavors" }
@ -26,7 +26,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
[features]
static = []

View file

@ -1 +0,0 @@
../../LICENSE-APACHE

201
mux/flavors/LICENSE-APACHE Normal file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1 +0,0 @@
../../LICENSE-MIT

23
mux/flavors/LICENSE-MIT Normal file
View file

@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show more