Compare commits

...

100 commits
main ... 0.15

Author SHA1 Message Date
Sebastian Dröge
dd745d2f18 Update versions to 0.15.7 2020-06-08 12:58:58 +03:00
Sebastian Dröge
604d58e153 Update CHANGELOG.md for 0.15.7 2020-06-08 12:58:37 +03:00
Sebastian Dröge
d465fa0140 audio: Add bindings for AudioBuffer
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/222
2020-06-08 12:29:16 +03:00
Sebastian Dröge
04a4d0a380 audio: Add bindings for AudioMeta
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/222
2020-06-08 12:23:20 +03:00
Sebastian Dröge
1924d3b222 audio: Add AudioInfo::is_valid() and guard against finfo being NULL when retrieving the audio format 2020-06-08 12:23:20 +03:00
Sebastian Dröge
d6b5126c87 audio: Add bindings for audio_buffer_truncate() 2020-06-08 12:23:20 +03:00
Sebastian Dröge
71bb12564f audio: Move audio_buffer_clip() to functions module 2020-06-08 12:23:20 +03:00
Sebastian Dröge
ed0f1140b8 video: Don't unmap VideoFrameRefs that are just borrowed 2020-06-08 12:23:20 +03:00
Sebastian Dröge
997dad0f11 video: Check that the passed in video info is valid when creating a VideoFrame 2020-06-08 12:23:20 +03:00
Sebastian Dröge
de0f228075 video: Add VideoInfo::is_valid() and guard against finfo being NULL when retrieving the video format 2020-06-08 12:23:20 +03:00
Thiago Sousa Santos
51024345f8 rtp_buffer: wrap more methods
* extension data
* onebyte/twobytes extension
* ssrc
* csrc
* get_payload
2020-06-08 12:23:20 +03:00
jneem
2c4de19067 README.md: Add remark about pkg-config in MSYS2
Mention (and link to) the known issue about the MSYS2 version of pkg-config.
2020-06-08 12:23:20 +03:00
Sebastian Dröge
afe2b18018 gstreamer/iterator: Store filter types in a hash table
Static variables in generic functions only exist once and not once per
type parameter, so before we were only able to register exactly one
filter type.
2020-06-08 12:23:20 +03:00
Sebastian Dröge
0feadec9fe video: Add VideoAffineTransformationMeta, CropMeta and RegionOfInterestMeta
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/218
2020-06-08 12:23:20 +03:00
Sebastian Dröge
06505ad74d gstreamer: Add bindings for ProtectionMeta 2020-06-08 12:11:56 +03:00
Sebastian Dröge
2868bb3033 Update version to 0.15.6 2020-05-28 13:14:03 +03:00
Sebastian Dröge
570c1d014a Update CHANGELOG.md for 0.15.6 2020-05-28 13:13:38 +03:00
Sebastian Dröge
d21953ba66 Add various glib::Value trait impls for borrowed versions of types
For SDPMessageRef, StructureRef and CapsFeatureRef this involves the
conversion from and to a glib::Value. Specifically this means that e.g.
a StructureRef can be retrieved from a glib::Value via borrowing, i.e.
without creating a copy of the structure first.

For all mini objects only retrieval from a glib::Value is implemented as
the other direction would allow to get multiple immutable references
from a mutable reference without borrowing, which is not allowed and
would make it possible to observe a mini object changing while having an
immutable reference to it.
2020-05-27 15:24:08 +03:00
Sebastian Dröge
c2acb000b1 gstreamer/promise: Convert None promise replies to an empty structure in the change_func
webrtcbin likes to put a NULL structure into the reply under some
circumstances when the promise successfully resolved.

See https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1300
for details.
2020-05-27 15:24:08 +03:00
Sebastian Dröge
8e96888c63 video/videoconverter: Add TryFrom<Structure> and inverse From impl for VideoConverterConfig
Allows converting between both types interchangebly.
2020-05-27 15:24:08 +03:00
Mathieu Duponchelle
487970f620 gstreamer-video: expose caps features 2020-05-27 15:24:08 +03:00
Sebastian Dröge
12063ec83a video: Add bindings for guess_framerate() 2020-05-27 14:51:21 +03:00
Sebastian Dröge
c35a7caf42 video: Add bindings for calculate_display_ratio() 2020-05-27 14:51:21 +03:00
Sebastian Dröge
b9d1669c0d gstreamer/event: Add setter/getter for the gst::Stream in the stream-start event 2020-05-27 14:51:21 +03:00
Sebastian Dröge
122ccafd1a gstreamer/gstreamer-app: Don't store strong references in futures Stream/Sink adapters
This applies to the ones of the appsink, appsrc and bus. If we would
store a strong reference then they would keep alive the underlying
object forever even if their pipeline disappeared in the meantime.

Like this e.g. the bus stream would start returning None once the bus
was destroyed, similar to how other channels are working in Rust.
2020-05-27 14:51:21 +03:00
Sebastian Dröge
215dbe53c6 video: Assert if caption data passed when adding caption metas is empty
It will cause a critical warning otherwise.
2020-05-27 14:51:21 +03:00
Guillaume Desmottes
605898277f gstreamer: caps: add new_from_iter(_with_features)() 2020-05-27 14:51:21 +03:00
Guillaume Desmottes
b724e0c46c gstreamer: structure: add new_from_iter()
Allow to easily create a new Structure from an iterator.

Fix #250
2020-05-27 13:28:42 +03:00
Sebastian Dröge
d623bcdce5 gstreamer: Add Stream::debug() and StreamCollection::debug()
These provide more helpful debug output than just the pointer when
printing.
2020-05-27 13:28:22 +03:00
Sebastian Dröge
43e7f9f589 Update version to 0.15.5 2020-05-03 09:52:04 +03:00
Sebastian Dröge
4a8b904c6c Update CHANGELOG.md for 0.15.5 2020-05-03 09:51:01 +03:00
Sebastian Dröge
a1fbb359ea rtsp-server: Use log() instead of log_with_object() for now in the 0.15 branch
See 8077b7ac82
2020-04-30 11:22:11 +03:00
Sebastian Dröge
451c198cec Run everything through cargo fmt again 2020-04-30 11:01:25 +03:00
Guillaume Desmottes
ef012b9445 rtp: add dox feature 2020-04-30 10:59:16 +03:00
alleynb
923daa04b9 tutorials/tutorial-6: Improve caps output
Fixed printing of pad capabilites in tutorial6.
Output now displays the capability lists eg:
  layout:{ (string)interleaved, (string)non-interleaved }

The origional broken output was being displayed as:
  layout:SendValue(Value(GString(Foreign(0x557140c99d00, 40))))
2020-04-30 10:58:52 +03:00
Sebastian Dröge
774281a0c9 gstreamer: Add some API to calculate the next state convert state changes into their component states and back 2020-04-30 10:58:34 +03:00
Guillaume Gomez
fdf56b1235 gstreamer*: Add missing manual traits 2020-04-30 10:57:26 +03:00
Sebastian Dröge
d23d313b39 gstreamer: Add Element::get_current_clock_time() and ::get_current_running_time()
This was added in GStreamer 1.18 but we can easily implement it
ourselves here for the time being and for older versions.
2020-04-30 10:57:12 +03:00
Sebastian Dröge
c8ac89ec2b gstreamer-audio: Simplify audio clipping meta test a bit 2020-04-30 10:57:03 +03:00
Sebastian Dröge
96c83bc0e6 gstreamer: Add bindings for Plugin::get_plugin_name()
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/248
2020-04-30 10:57:03 +03:00
Sebastian Dröge
26beed7277 gstreamer-audio: Follow-up commit for updating audio clipping meta tests 2020-04-30 10:57:03 +03:00
Andrew Eikum
d9a06b31de gstreamer-audio: Add support for GstAudioClippingMeta 2020-04-30 10:57:03 +03:00
Sebastian Dröge
5479b23eb9 Use mem::ManuallyDrop instead of mem::forget() everywhere
It makes the intentions clearer and potentially results in simpler
assembly, at least in debug builds.
2020-04-30 10:56:38 +03:00
Sebastian Dröge
973ac7344e gstreamer/buffer: Add tests for various memory related buffer API 2020-04-30 10:56:20 +03:00
Sebastian Dröge
2bbfbb5842 gstreamer/buffer: Add iterators for iterating over the memories inside a buffer
With immutable, mutable and owned variant.
2020-04-30 10:56:13 +03:00
Sebastian Dröge
0749250743 gstreamer/buffer: Add BufferRef::peek_memory_mut() function
This gives a mutable reference to the given memory and fails if the
memory is not actually writable.
2020-04-30 10:56:02 +03:00
Sebastian Dröge
e8c5f5fb6b gstreamer/log: Replace % with %% in the debug message
Otherwise gst_debug_log() will look for further arguments that don't
exist, and will likely crash or worse.
2020-04-30 10:54:09 +03:00
Sebastian Dröge
8077b7ac82 Revert "gstreamer/log: Allow any glib::Object as target for logging"
This reverts commit 67e6afc628.

Unfortunately this breaks the API due to how subclassing is modeled in
the GObject bindings.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/issues/251
2020-04-30 10:19:55 +03:00
Sebastian Dröge
ef77a5ae92 Update versions to 0.15.4 2020-03-09 23:01:42 +02:00
Sebastian Dröge
079095b55c Update CHANGELOG.md for 0.15.4 2020-03-09 23:01:42 +02:00
Sebastian Dröge
2599acc681 gstreamer/caps: Assert on ANY caps in fixate() and work around bug in handling EMPTY caps
See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/388
2020-03-09 23:01:42 +02:00
Sebastian Dröge
671605d8ac Minor cleanup in various places 2020-03-09 22:49:51 +02:00
Sebastian Dröge
01da01c9e6 app: Handle panicking callbacks by converting into an error message
And never calling the callbacks again but instead just failing.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/241
2020-03-09 22:49:51 +02:00
Sebastian Dröge
4e30798ac7 Add #[must_use] attribute to mutex guards / stream lock
It's usually a mistake if creating one of these and immediately dropping
them again as that would immediately unlock the mutex again.
2020-03-09 22:49:51 +02:00
Sebastian Dröge
ed80467ff6 Make sure to hold MutexGuard for the remainder of the function in various places
Not assigning it to a variable would cause it to be dropped immediately
and the lock to be released again immediately.
2020-03-09 22:49:50 +02:00
Sebastian Dröge
19295f75b5 rtsp_server: Add example making use of subclassing RTSPMediaFactory and RTSPMedia 2020-03-09 22:49:50 +02:00
Sebastian Dröge
e02ec7a8c9 rtsp_server: Add support for subclassing RTSPServer and RTSPClient 2020-03-09 22:49:50 +02:00
Sebastian Dröge
21ffeb5e82 rtsp_server: Add subclassing support for MediaFactory and Media 2020-03-09 22:49:50 +02:00
Sebastian Dröge
aa86c804bd rtsp_server/media: Implement take_pipeline() manually
Because of floating reference problems in the API that have to be worked
around.
2020-03-09 22:42:49 +02:00
Sebastian Dröge
38071f1897 rtsp-server: Add bindings for RTSPThread 2020-03-09 22:42:42 +02:00
Sebastian Dröge
d429fad50d gstreamer/base_transform: Add support for implementing prepare_output_buffer()
This requires some acrobatics due to inconsistent ownership handling of
the buffers on the C side.
2020-03-09 22:42:35 +02:00
Sebastian Dröge
67e6afc628 gstreamer/log: Allow any glib::Object as target for logging
gst::Object is not actually required. For plain glib::Objects only the
pointer address is printed instead of a name but it works fine.
2020-03-09 22:42:26 +02:00
Sebastian Dröge
0e69898faf Update version to 0.15.3 2020-02-15 10:46:41 +01:00
Sebastian Dröge
45b7676d02 Update CHANGELOG.md for 0.15.3 2020-02-15 10:46:14 +01:00
Sebastian Dröge
315a59e47d Allow changing bus sync handler and appsink/src callbacks when running with 1.16.3 or newer
Previously it was not thread-safe to change them and could lead to
crashes but with 1.16.3 it is now.

Unsetting the bus sync handler before 1.16.3 will have no effect at all,
setting a new bus sync handler or appsink/src callbacks will panic.

This partially reverts 2f88dc6576
2020-02-15 10:35:35 +01:00
Sebastian Dröge
3b56f470e5 Only allow setting Bus sync handler and AppSrc/Sink callbacks once
Re-setting them is not thread-safe and can cause segfaults or worse.

See https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/506
and https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/issues/729
2020-02-15 10:33:53 +01:00
Valmir Pretto
0d34e70e3e gstreamer-app: AppSink futures adapter
An adapter for AppSink that adds futures capabilities to it in the form of a Stream.
2020-02-15 10:29:56 +01:00
Valmir Pretto
7a05ff52af gstreamer-app: AppSrc futures adapter
An adapter for AppSrc that adds futures capabilities to it in the form of a Sink.
2020-02-15 10:29:49 +01:00
Valmir Pretto
66338881e8 gstreamer: BusStream tests 2020-02-15 10:29:43 +01:00
Valmir Pretto
5beb871419 gstreamer: BusStream cleanup
A few small readability changes
2020-02-15 10:29:38 +01:00
Sebastian Dröge
462f4ac3b8 video/video-info: Don't use bool return of gst_video_info_set_format()/align() when running with GStreamer < 1.11.1
The bool return value was added in 1.11.1 and using the return value
with older versions gives a random value that might be true or false,
and then causes spurious errors.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/236#note_399872
2020-02-15 10:15:03 +01:00
Sebastian Dröge
a61c50c21a base/base_transform: Fix minor clippy warning
warning: called `map(f)` on an Option value where `f` is a unit closure
   --> gstreamer-base/src/subclass/base_transform.rs:683:13
    |
683 | /             (*parent_class)
684 | |                 .before_transform
685 | |                 .map(|f| f(element.to_glib_none().0, inbuf.as_ptr() as *mut _));
    | |_______________________________________________________________________________^
    |
    = note: `#[warn(clippy::option_map_unit_fn)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
2020-02-15 10:14:54 +01:00
Sebastian Dröge
e6d3ac4e4b base/base_transform: Add before_transform/copy_metadata/transform_meta support for subclasses 2020-02-15 10:14:46 +01:00
Sebastian Dröge
89b9dc80dc base/base_transform: Add submit_input_buffer/generate_output support for subclasses
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/155
2020-02-15 10:14:41 +01:00
Sebastian Dröge
9b593f626b gstreamer/promise: Add Future constructor for Promise
This returns a tuple that basically works like a oneshot channel: the
Promise acts as the "sender" and once the promise resolves the
"receiver" contains the result.
2020-02-15 10:14:35 +01:00
Sebastian Dröge
605d4fbf24 video: Add Future variant of convert_sample_async()
Requires to be spawned on the GLib main context futures executor as the
function itself requires a GLib main context to call the callback.
2020-02-15 10:14:22 +01:00
Sebastian Dröge
7fa810873f audio/stream_align: Don't require mutable references in getters 2020-02-15 10:13:48 +01:00
Sebastian Dröge
0ffd86c37e base/flow_combiner: UniqueFlowCombiner::clear() should take &mut self
It's modifying the state of the combiner and requires a unique, mutable
reference.
2020-02-15 10:13:40 +01:00
Sebastian Dröge
ea38e022be video: Add VideoConverter bindings 2020-02-15 10:11:41 +01:00
Sebastian Dröge
23fdd2c5ad video: Autogenerate bindings for a few more enums 2020-02-15 10:11:35 +01:00
Sebastian Dröge
3ae8b48400 video/video-frame: Implement immutable frame functions more generically 2020-02-15 10:11:25 +01:00
Sebastian Dröge
188ef1e242 impl Clone for BufferPoolConfig and PlayerConfig 2020-02-15 10:11:19 +01:00
Sebastian Dröge
a47a6ea76c Update versions to 0.15.2 2020-01-30 00:20:38 +02:00
Sebastian Dröge
e5397f5a33 Update CHANGELOG.md for 0.15.2 2020-01-30 00:20:22 +02:00
Sebastian Dröge
41663f15ef bus: Don't use the bus sync handler in the bus Stream to notify about messages being available
This is racy and can cause the consumer of the messages to never be
woken up anymore:

1. Waker is stored because no message on the bus
2. Sync handler is called, waker is woken up
3. Bus is polled again and no message is on it (yet),
   new waker is registered
4. Bus stores the message from 2. in its queue (after
   the sync handler has returned BusSyncReply::Pass)
5. No new message ever appears on the bus because all
   this happened for the very last message

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/235
2020-01-28 23:43:41 +02:00
Sebastian Dröge
8a9be64c3f Update version to 0.15.1 2020-01-23 08:06:17 +02:00
Sebastian Dröge
3a8ae5ddd4 Update CHANGELOG.md for 0.15.1 2020-01-23 08:06:00 +02:00
Sebastian Dröge
fc1e909925 gstreamer/childproxy: Fix unused variable compiler warnings 2020-01-22 22:31:10 +02:00
Sebastian Dröge
5577e8a457 Don't derive Debug impls for generic types where the type parameters don't have to impl Debug themselves
This allows to use MappedBuffer and similar types to be properly
debug-printed.

Also change VideoFrame/VideoFrameRef/RTPBuffer from a tuple struct to a
struct with proper field names to make the code easier to understand.
2020-01-22 22:31:10 +02:00
Sebastian Dröge
456ad9fb4a gstreamer/childproxy: Don't require implementing child_added/removed() signal vfuncs
Most implementers will not care about default handlers for these signals
so requiring to implement them is only unnecessarily verbose.
2020-01-22 22:31:10 +02:00
Sebastian Dröge
b127f93cb9 bus: Take the mutex before popping messages for the bus stream
Otherwise a message might arrive between popping, getting None and
locking the mutex for storing the waker. In that case we would never
be woken up.
2020-01-22 22:31:10 +02:00
Sebastian Dröge
db1c341cdf gstreamer/element: Add call_async_future() that returns a future
The future would resolve into the return value of the closure that is
called asynchronously on the thread pool, and allows asynchronous
awaiting for it to finish.

  let res = element.call_async(|element| {
      element.set_state(gst::State::Playing)
  }).await;

  assert_eq!(res, Ok(gst::StateChangeSuccess::Success))
2020-01-22 22:31:10 +02:00
Guillaume Desmottes
c29a7638d3 gstreamer: caps: BuilderFull: prevent adding features if using any
Rework the API to statically prevent users adding extra features if the
builder has been created with builder_full_with_any_features(). It
doesn't make sense to add extra features if all are already included.
2020-01-22 19:44:43 +02:00
Guillaume Desmottes
7f07bac0c7 gstreamer: add Caps::builder_full()
API to create a caps containing multiple structures.
Fix #231
2020-01-22 19:44:15 +02:00
Sebastian Dröge
3425bcfe9d gstreamer: Add getters/setters for BinFlags and fix flags API for PadFlags
The BinFlags API was accidentally used for PadFlags, allowing to set
BinFlags on pads which is not very useful.
2020-01-22 19:44:09 +02:00
Sebastian Dröge
28cc573875 Run clippy on --all-targets
Also ignore clippy::missing_safety_doc for now.
2019-12-23 13:40:58 +02:00
Sebastian Dröge
fedf4b664a Fix various new clippy warnings from 1.40 2019-12-23 13:40:58 +02:00
Sebastian Dröge
d2e508eca1 video: Use static inner lifetime for VideoCodecState<Readable>
It does not borrow from anything else and can be safely stored away.

Only the VideoCodecState<InNegotiation> is actively borrowing from the
element to ensure that it is only modified during negotiation in a safe
way.
2019-12-23 13:15:34 +02:00
Sebastian Dröge
d62d788630 gstreamer-rtp: Add README.md 2019-12-19 00:37:51 +02:00
Sebastian Dröge
6fd13cc807 Update versions of all dependencies and point to releases instead of GIT 2019-12-18 19:15:21 +02:00
145 changed files with 11178 additions and 789 deletions

View file

@ -137,12 +137,12 @@ clippy:
FEATURES=v1_16
fi
cargo clippy --color=always --manifest-path $crate/Cargo.toml --features=$FEATURES -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless
cargo clippy --color=always --manifest-path $crate/Cargo.toml --features=$FEATURES --all-targets -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless -A clippy::missing_safety_doc
done
# And also run over all the examples/tutorials
- |
cargo clippy --color=always --manifest-path examples/Cargo.toml --bins --examples --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless
cargo clippy --color=always --manifest-path tutorials/Cargo.toml --bins --examples --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless
cargo clippy --color=always --manifest-path examples/Cargo.toml --all-targets --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless -A clippy::missing_safety_doc
cargo clippy --color=always --manifest-path tutorials/Cargo.toml --all-targets --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless -A clippy::missing_safety_doc
audit:
extends: '.tarball_setup'

View file

@ -48,8 +48,6 @@ generate = [
"Gst.BufferCopyFlags",
"Gst.PadMode",
"Gst.SchedulingFlags",
"Gst.ChildProxy",
"Gst.TagSetter",
"Gst.QOSType",
"Gst.TocSetter",
"Gst.ClockType",
@ -60,7 +58,6 @@ generate = [
"Gst.PipelineFlags",
"Gst.PluginFlags",
"Gst.MemoryFlags",
"Gst.Allocator",
"Gst.PadLinkCheck",
"Gst.DebugLevel",
"Gst.DebugColorFlags",
@ -86,9 +83,9 @@ manual = [
]
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
name = "Gst.Allocator"
status = "generate"
manual_traits = ["AllocatorExtManual"]
[[object]]
name = "Gst.Bin"
@ -182,6 +179,16 @@ status = "generate"
name = "Serialize, Deserialize"
cfg_condition = "feature = \"ser_de\""
[[object]]
name = "Gst.ChildProxy"
status = "generate"
manual_traits = ["ChildProxyExtManual"]
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
[[object]]
name = "Gst.Format"
status = "generate"
@ -198,6 +205,11 @@ status = "generate"
name = "Serialize, Deserialize"
cfg_condition = "feature = \"ser_de\""
[[object]]
name = "Gst.TagSetter"
status = "generate"
manual_traits = ["TagSetterExtManual"]
[[object]]
name = "Gst.TocScope"
status = "generate"
@ -329,6 +341,7 @@ ref_mode = "ref"
[[object]]
name = "Gst.Clock"
status = "generate"
manual_traits = ["ClockExtManual"]
[[object.function]]
name = "set_master"
[object.function.return]
@ -360,6 +373,7 @@ status = "generate"
[[object]]
name = "Gst.Element"
status = "generate"
manual_traits = ["ElementExtManual"]
[[object.function]]
name = "make_from_uri"
[object.function.return]
@ -562,6 +576,7 @@ final_type = true
[[object]]
name = "Gst.DeviceProvider"
status = "generate"
manual_traits = ["DeviceProviderExtManual"]
[[object.function]]
name = "get_bus"
[object.function.return]
@ -584,6 +599,7 @@ status = "generate"
[[object]]
name = "Gst.DeviceMonitor"
status = "generate"
manual_traits = ["DeviceMonitorExtManual"]
[[object.function]]
name = "new"
# Work-around for 1.14 switch from transfer-floating to transfer-full
@ -671,6 +687,7 @@ trait_name = "GstObjectExt"
[[object]]
name = "Gst.Pad"
status = "generate"
manual_traits = ["PadExtManual"]
[[object.function]]
name = "link_maybe_ghosting"
[object.function.return]
@ -923,6 +940,7 @@ status = "generate"
[[object]]
name = "Gst.ProxyPad"
status = "generate"
manual_traits = ["ProxyPadExtManual"]
[[object.function]]
name = "chain_default"
@ -1035,6 +1053,7 @@ final_type = true
[[object]]
name = "Gst.PluginFeature"
status = "generate"
manual_traits = ["PluginFeatureExtManual"]
[[object.function]]
pattern = "list_.*"
ignore = true
@ -1083,6 +1102,7 @@ final_type = true
[[object]]
name = "Gst.BufferPool"
status = "generate"
manual_traits = ["BufferPoolExtManual"]
[[object.function]]
pattern = "config_.*"
# A different type

View file

@ -144,9 +144,17 @@ status = "generate"
# bool does not signal error
ignore = true
[[object.function]]
pattern = "get_.*"
[[object.function.parameter]]
name = "align"
const = true
[[object]]
name = "GstAudio.AudioDecoder"
status = "generate"
manual_traits = ["AudioDecoderExtManual"]
[[object.function]]
name = "finish_frame"
ignore = true
@ -184,6 +192,7 @@ status = "generate"
[[object]]
name = "GstAudio.AudioEncoder"
status = "generate"
manual_traits = ["AudioEncoderExtManual"]
[[object.function]]
name = "finish_frame"

View file

@ -113,6 +113,7 @@ concurrency = "none"
[[object]]
name = "GstBase.BaseSink"
status = "generate"
manual_traits = ["BaseSinkExtManual"]
[[object.function]]
name = "wait"
@ -137,6 +138,7 @@ status = "generate"
[[object]]
name = "GstBase.BaseSrc"
status = "generate"
manual_traits = ["BaseSrcExtManual"]
[[object.function]]
name = "set_caps"
@ -171,6 +173,7 @@ status = "generate"
[[object]]
name = "GstBase.BaseTransform"
status = "generate"
manual_traits = ["BaseTransformExtManual"]
[[object.function]]
name = "update_src_caps"
@ -182,6 +185,7 @@ status = "generate"
name = "GstBase.Aggregator"
status = "generate"
version = "1.14"
manual_traits = ["AggregatorExtManual"]
[[object.function]]
name = "finish_buffer"
@ -201,6 +205,7 @@ version = "1.14"
name = "GstBase.AggregatorPad"
status = "generate"
version = "1.14"
manual_traits = ["AggregatorPadExtManual"]
[[object]]
name = "GstBase.*"
@ -278,6 +283,7 @@ ref_mode = "ref"
[[object]]
name = "GstBase.BaseParse"
status = "generate"
manual_traits = ["BaseParseExtManual"]
[[object.function]]
name = "finish_frame"
ignore = true

View file

@ -272,6 +272,7 @@ status = "generate"
[[object]]
name = "GES.TimelineElement"
status = "generate"
manual_traits = ["TimelineElementExtManual"]
[[object.function]]
name = "ripple"
[object.function.return]

View file

@ -94,6 +94,7 @@ ref_mode = "ref"
[[object]]
name = "GstGL.GLContext"
status = "generate"
manual_traits = ["GLContextExtManual"]
[[object.function]]
name = "new_wrapped"

View file

@ -96,6 +96,11 @@ name = "GstRtspServer.RTSPToken"
status = "manual"
ref_mode = "ref"
[[object]]
name = "GstRtspServer.RTSPThread"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.ClockTime"
status = "manual"
@ -104,6 +109,7 @@ conversion_type = "scalar"
[[object]]
name = "GstRtspServer.RTSPServer"
status = "generate"
manual_traits = ["RTSPServerExtManual"]
[[object.function]]
name = "attach"
@ -122,6 +128,7 @@ status = "generate"
[[object]]
name = "GstRtspServer.RTSPClient"
status = "generate"
manual_traits = ["RTSPClientExtManual"]
[[object.function]]
name = "attach"
@ -130,6 +137,7 @@ status = "generate"
[[object]]
name = "GstRtspServer.RTSPStream"
status = "generate"
manual_traits = ["RTSPStreamExtManual"]
[[object.function]]
name = "recv_rtcp"
@ -193,6 +201,7 @@ concurrency = "send"
name = "GstRtspServer.RTSPStreamTransport"
status = "generate"
concurrency = "none"
manual_traits = ["RTSPStreamTransportExtManual"]
[[object.function]]
name = "recv_data"
@ -217,6 +226,7 @@ concurrency = "none"
[[object]]
name = "GstRtspServer.RTSPAddressPool"
status = "generate"
manual_traits = ["RTSPAddressPoolExtManual"]
[[object.function]]
name = "add_range"
@ -235,6 +245,7 @@ status = "generate"
[[object]]
name = "GstRtspServer.RTSPMedia"
status = "generate"
manual_traits = ["RTSPMediaExtManual"]
[[object.function]]
name = "suspend"
@ -246,14 +257,24 @@ status = "generate"
[object.function.return]
bool_return_is_error = "Failed to unprepare media"
[[object.function]]
name = "prepare"
[object.function.return]
bool_return_is_error = "Failed to prepare media"
[[object.function]]
name = "unsuspend"
[object.function.return]
bool_return_is_error = "Failed to unsuspend media"
[[object.function]]
name = "take_pipeline"
ignore = true
[[object]]
name = "GstRtspServer.RTSPMediaFactory"
status = "generate"
manual_traits = ["RTSPMediaFactoryExtManual"]
[[object.function]]
name = "add_role_from_structure"
@ -282,6 +303,7 @@ status = "generate"
[[object]]
name = "GstRtspServer.RTSPSessionPool"
status = "generate"
manual_traits = ["RTSPSessionPoolExtManual"]
[[object.function]]
name = "remove"
@ -301,6 +323,8 @@ status = "generate"
[[object]]
name="GstRtspServer.RTSPAuth"
status="generate"
manual_traits = ["RTSPAuthExtManual"]
[[object.function]]
name = "check"
[object.function.return]

View file

@ -40,6 +40,13 @@ generate = [
"GstVideo.VideoBufferPool",
"GstVideo.VideoPackFlags",
"GstVideo.VideoBufferFlags",
"GstVideo.VideoAlphaMode",
"GstVideo.VideoChromaMode",
"GstVideo.VideoMatrixMode",
"GstVideo.VideoGammaMode",
"GstVideo.VideoPrimariesMode",
"GstVideo.VideoResamplerMethod",
"GstVideo.VideoDitherMethod",
]
manual = [
@ -81,6 +88,7 @@ ref_mode = "ref"
[[object]]
name = "GstVideo.VideoOverlay"
status = "generate"
manual_traits = ["VideoOverlayExtManual"]
[[object.function]]
name = "set_render_rectangle"
@ -90,6 +98,7 @@ status = "generate"
[[object]]
name = "GstVideo.VideoDecoder"
status = "generate"
manual_traits = ["VideoDecoderExtManual"]
[[object.function]]
name = "allocate_output_frame"
@ -168,6 +177,7 @@ status = "generate"
[[object]]
name = "GstVideo.VideoEncoder"
status = "generate"
manual_traits = ["VideoEncoderExtManual"]
[[object.function]]
name = "allocate_output_frame"

View file

@ -121,6 +121,11 @@ If you wish to install the gstreamer-player sub-crate, make sure the
version of these libraries is >= 1.12. Otherwise, a version >= 1.8 is
sufficient.
Note that the version of `pkg-config` included in `MSYS2` is
[known to have problems](https://github.com/rust-lang/pkg-config-rs/issues/51#issuecomment-346300858)
compiling GStreamer, so you may need to install another version. One option
would be [`pkg-config-lite`](https://sourceforge.net/projects/pkgconfiglite/).
#### GStreamer Binaries
You need to download the *two* `.msi` files for your platform from the

View file

@ -5,29 +5,30 @@ license = "MIT"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
[dependencies]
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-gl = { path = "../gstreamer-gl", optional = true }
gstreamer-app = { path = "../gstreamer-app" }
gstreamer-audio = { path = "../gstreamer-audio" }
gstreamer-base = { path = "../gstreamer-base" }
gstreamer-video = { path = "../gstreamer-video" }
gstreamer-pbutils = { path = "../gstreamer-pbutils" }
gstreamer-player = { path = "../gstreamer-player", optional = true }
gstreamer-editing-services = { path = "../gstreamer-editing-services", optional = true }
gstreamer-rtsp = { path = "../gstreamer-rtsp", optional = true }
gstreamer-rtsp-server = { path = "../gstreamer-rtsp-server", optional = true }
gstreamer-rtsp-server-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"], optional = true }
gtk = { git = "https://github.com/gtk-rs/gtk", optional = true }
gdk = { git = "https://github.com/gtk-rs/gdk", optional = true }
gio = { git = "https://github.com/gtk-rs/gio", optional = true }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-gl = { version = "0.15", path = "../gstreamer-gl", optional = true }
gstreamer-app = { version = "0.15", path = "../gstreamer-app" }
gstreamer-audio = { version = "0.15", path = "../gstreamer-audio" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
gstreamer-video = { version = "0.15", path = "../gstreamer-video" }
gstreamer-pbutils = { version = "0.15", path = "../gstreamer-pbutils" }
gstreamer-player = { version = "0.15", path = "../gstreamer-player", optional = true }
gstreamer-editing-services = { version = "0.15", path = "../gstreamer-editing-services", optional = true }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp", optional = true }
gstreamer-rtsp = { version = "0.15", path = "../gstreamer-rtsp", optional = true }
gstreamer-rtsp-server = { version = "0.15", path = "../gstreamer-rtsp-server", optional = true }
gstreamer-rtsp-server-sys = { version = "0.8", features = ["v1_8"], optional = true }
gtk = { version = "0.8", optional = true }
gdk = { version = "0.12", optional = true }
gio = { version = "0.8", optional = true }
futures = "0.3"
byte-slice-cast = "0.3"
failure = "0.1"
failure_derive = "0.1"
cairo-rs = { git = "https://github.com/gtk-rs/cairo", features=["use_glib"], optional = true }
pango = { git = "https://github.com/gtk-rs/pango", optional = true }
pangocairo = { git = "https://github.com/gtk-rs/pangocairo", optional = true }
cairo-rs = { version = "0.8", features=["use_glib"], optional = true }
pango = { version = "0.8", optional = true }
pangocairo = { version = "0.9", optional = true }
glutin = { version = "0.21", optional = true }
winit = { version = "0.19", optional = true }
lazy_static = "1.0"
@ -43,7 +44,7 @@ gtksink = ["gtk", "gio"]
gtkvideooverlay = ["gtk", "gdk", "gio"]
gtkvideooverlay-x11 = ["gtkvideooverlay"]
gtkvideooverlay-quartz = ["gtkvideooverlay"]
gst-rtsp-server = ["gstreamer-rtsp-server"]
gst-rtsp-server = ["gstreamer-rtsp-server", "gstreamer-rtsp", "gstreamer-sdp"]
gst-rtsp-server-record = ["gstreamer-rtsp-server-sys", "gstreamer-rtsp-server", "gstreamer-rtsp", "gio"]
v1_10 = ["gstreamer/v1_10"]
pango-cairo = ["pango", "pangocairo", "cairo-rs"]
@ -110,6 +111,10 @@ name = "rtpfecserver"
name = "rtsp-server"
required-features = ["gst-rtsp-server"]
[[bin]]
name = "rtsp-server-subclass"
required-features = ["gst-rtsp-server"]
[[bin]]
name = "tagsetter"

View file

@ -168,7 +168,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -160,7 +160,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -281,7 +281,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into()),
@ -295,7 +295,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -311,7 +311,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into()),
@ -325,7 +325,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -594,7 +594,7 @@ impl App {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -144,7 +144,6 @@ fn create_ui(app: &gtk::Application) {
// simplifies the code within the callback. What this actually does, however, is creating
// a memory leak.
let video_overlay = sink
.clone()
.dynamic_cast::<gst_video::VideoOverlay>()
.unwrap()
.downgrade();

View file

@ -213,7 +213,6 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
})
.unwrap();
let drawer_clone = drawer.clone();
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
// be called when the sink that we render to does not support resizing the image
// itself - but the user just changed the window-size. The element after the overlay
@ -226,7 +225,6 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
let _overlay = args[0].get::<gst::Element>().unwrap().unwrap();
let caps = args[1].get::<gst::Caps>().unwrap().unwrap();
let drawer = &drawer_clone;
let mut drawer = drawer.lock().unwrap();
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
@ -257,7 +255,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -275,7 +275,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -146,9 +146,8 @@ fn example_main() -> Result<(), Error> {
let sinkpad = get_static_pad(&sink, "sink")?;
srcpad.link(&sinkpad)?;
let convclone = conv.clone();
src.connect_pad_added(move |decodebin, src_pad| {
match connect_decodebin_pad(&src_pad, &convclone) {
src.connect_pad_added(
move |decodebin, src_pad| match connect_decodebin_pad(&src_pad, &conv) {
Ok(_) => (),
Err(err) => {
gst_element_error!(
@ -158,8 +157,8 @@ fn example_main() -> Result<(), Error> {
["{}", err]
);
}
}
});
},
);
let video_caps = gst::Caps::new_simple("video/x-raw", &[]);
@ -199,7 +198,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -0,0 +1,481 @@
// This example demonstrates how to set up a rtsp server using GStreamer
// and extending the behaviour by subclass RTSPMediaFactory and RTSPMedia.
// For this, the example creates a videotestsrc pipeline manually to be used
// by the RTSP server for providing data, and adds a custom attribute to the
// SDP provided to the client.
//
// It also comes with a custom RTSP server/client subclass for hooking into
// the client machinery and printing some status.
extern crate gstreamer as gst;
extern crate gstreamer_rtsp as gst_rtsp;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
extern crate gstreamer_sdp as gst_sdp;
use gst_rtsp_server::prelude::*;
#[macro_use]
extern crate glib;
extern crate failure;
use failure::Error;
#[macro_use]
extern crate failure_derive;
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Fail)]
#[fail(display = "Could not get mount points")]
struct NoMountPoints;
#[derive(Debug, Fail)]
#[fail(display = "Usage: {} LAUNCH_LINE", _0)]
struct UsageError(String);
fn main_loop() -> Result<(), Error> {
let main_loop = glib::MainLoop::new(None, false);
let server = server::Server::new();
// Much like HTTP servers, RTSP servers have multiple endpoints that
// provide different streams. Here, we ask our server to give
// us a reference to his list of endpoints, so we can add our
// test endpoint, providing the pipeline from the cli.
let mounts = server.get_mount_points().ok_or(NoMountPoints)?;
// Next, we create our custom factory for the endpoint we want to create.
// The job of the factory is to create a new pipeline for each client that
// connects, or (if configured to do so) to reuse an existing pipeline.
let factory = media_factory::Factory::new();
// This setting specifies whether each connecting client gets the output
// of a new instance of the pipeline, or whether all connected clients share
// the output of the same pipeline.
// If you want to stream a fixed video you have stored on the server to any
// client, you would not set this to shared here (since every client wants
// to start at the beginning of the video). But if you want to distribute
// a live source, you will probably want to set this to shared, to save
// computing and memory capacity on the server.
factory.set_shared(true);
// Now we add a new mount-point and tell the RTSP server to serve the content
// provided by the factory we configured above, when a client connects to
// this specific path.
mounts.add_factory("/test", &factory);
// Attach the server to our main context.
// A main context is the thing where other stuff is registering itself for its
// events (e.g. sockets, GStreamer bus, ...) and the main loop is something that
// polls the main context for its events and dispatches them to whoever is
// interested in them. In this example, we only do have one, so we can
// leave the context parameter empty, it will automatically select
// the default one.
let id = server.attach(None);
println!(
"Stream ready at rtsp://127.0.0.1:{}/test",
server.get_bound_port()
);
// Start the mainloop. From this point on, the server will start to serve
// our quality content to connecting clients.
main_loop.run();
glib::source_remove(id);
Ok(())
}
// Our custom media factory that creates a media input manually
mod media_factory {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our factory
pub struct Factory {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Factory {
const NAME: &'static str = "RsRTSPMediaFactory";
type ParentType = gst_rtsp_server::RTSPMediaFactory;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Factory {
// This macro provides some boilerplate.
glib_object_impl!();
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let factory = obj
.downcast_ref::<gst_rtsp_server::RTSPMediaFactory>()
.unwrap();
// All media created by this factory are our custom media type. This would
// not require a media factory subclass and can also be called on the normal
// RTSPMediaFactory.
factory.set_media_gtype(super::media::Media::static_type());
}
}
// Implementation of gst_rtsp_server::RTSPMediaFactory virtual methods
impl RTSPMediaFactoryImpl for Factory {
fn create_element(
&self,
_factory: &gst_rtsp_server::RTSPMediaFactory,
_url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element> {
// Create a simple VP8 videotestsrc input
let bin = gst::Bin::new(None);
let src = gst::ElementFactory::make("videotestsrc", None).unwrap();
let enc = gst::ElementFactory::make("vp8enc", None).unwrap();
// The names of the payloaders must be payX
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap();
// Configure the videotestsrc live
src.set_property("is-live", &true).unwrap();
// Produce encoded data as fast as possible
enc.set_property("deadline", &1i64).unwrap();
bin.add_many(&[&src, &enc, &pay]).unwrap();
gst::Element::link_many(&[&src, &enc, &pay]).unwrap();
Some(bin.upcast())
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPMediaFactory
glib_wrapper! {
pub struct Factory(
Object<
gst::subclass::ElementInstanceStruct<imp::Factory>,
subclass::simple::ClassStruct<imp::Factory>,
FactoryClass
>
) @extends gst_rtsp_server::RTSPMediaFactory;
match fn {
get_type => || imp::Factory::get_type().to_glib(),
}
}
// Factories must be Send+Sync, and ours is
unsafe impl Send for Factory {}
unsafe impl Sync for Factory {}
impl Factory {
// Creates a new instance of our factory
pub fn new() -> Factory {
glib::Object::new(Self::static_type(), &[])
.expect("Failed to create factory")
.downcast()
.expect("Created factory is of wrong type")
}
}
}
// Our custom media subclass that adds a custom attribute to the SDP returned by DESCRIBE
mod media {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our media
pub struct Media {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Media {
const NAME: &'static str = "RsRTSPMedia";
type ParentType = gst_rtsp_server::RTSPMedia;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Media {
// This macro provides some boilerplate.
glib_object_impl!();
}
// Implementation of gst_rtsp_server::RTSPMedia virtual methods
impl RTSPMediaImpl for Media {
fn setup_sdp(
&self,
media: &gst_rtsp_server::RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &gst_rtsp_server::subclass::rtsp_media::SDPInfo,
) -> Result<(), gst::LoggableError> {
self.parent_setup_sdp(media, sdp, info)?;
sdp.add_attribute("my-custom-attribute", Some("has-a-value"));
Ok(())
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPMedia
glib_wrapper! {
pub struct Media(
Object<
gst::subclass::ElementInstanceStruct<imp::Media>,
subclass::simple::ClassStruct<imp::Media>,
MediaClass
>
) @extends gst_rtsp_server::RTSPMedia;
match fn {
get_type => || imp::Media::get_type().to_glib(),
}
}
// Medias must be Send+Sync, and ours is
unsafe impl Send for Media {}
unsafe impl Sync for Media {}
}
// Our custom RTSP server subclass that reports when clients are connecting and uses
// our custom RTSP client subclass for each client
mod server {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our server
pub struct Server {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Server {
const NAME: &'static str = "RsRTSPServer";
type ParentType = gst_rtsp_server::RTSPServer;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Server {
// This macro provides some boilerplate.
glib_object_impl!();
}
// Implementation of gst_rtsp_server::RTSPServer virtual methods
impl RTSPServerImpl for Server {
fn create_client(
&self,
server: &gst_rtsp_server::RTSPServer,
) -> Option<gst_rtsp_server::RTSPClient> {
let client = super::client::Client::new();
// Duplicated from the default implementation
client.set_session_pool(server.get_session_pool().as_ref());
client.set_mount_points(server.get_mount_points().as_ref());
client.set_auth(server.get_auth().as_ref());
client.set_thread_pool(server.get_thread_pool().as_ref());
Some(client.upcast())
}
fn client_connected(
&self,
server: &gst_rtsp_server::RTSPServer,
client: &gst_rtsp_server::RTSPClient,
) {
self.parent_client_connected(server, client);
println!("Client {:?} connected", client);
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPServer
glib_wrapper! {
pub struct Server(
Object<
gst::subclass::ElementInstanceStruct<imp::Server>,
subclass::simple::ClassStruct<imp::Server>,
ServerClass
>
) @extends gst_rtsp_server::RTSPServer;
match fn {
get_type => || imp::Server::get_type().to_glib(),
}
}
// Servers must be Send+Sync, and ours is
unsafe impl Send for Server {}
unsafe impl Sync for Server {}
impl Server {
// Creates a new instance of our factory
pub fn new() -> Server {
glib::Object::new(Self::static_type(), &[])
.expect("Failed to create server")
.downcast()
.expect("Created server is of wrong type")
}
}
}
// Our custom RTSP client subclass.
mod client {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our server
pub struct Client {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Client {
const NAME: &'static str = "RsRTSPClient";
type ParentType = gst_rtsp_server::RTSPClient;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Client {
// This macro provides some boilerplate.
glib_object_impl!();
}
// Implementation of gst_rtsp_server::RTSPClient virtual methods
impl RTSPClientImpl for Client {
fn closed(&self, client: &gst_rtsp_server::RTSPClient) {
self.parent_closed(client);
println!("Client {:?} closed", client);
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPClient
glib_wrapper! {
pub struct Client(
Object<
gst::subclass::ElementInstanceStruct<imp::Client>,
subclass::simple::ClassStruct<imp::Client>,
ClientClass
>
) @extends gst_rtsp_server::RTSPClient;
match fn {
get_type => || imp::Client::get_type().to_glib(),
}
}
// Clients must be Send+Sync, and ours is
unsafe impl Send for Client {}
unsafe impl Sync for Client {}
impl Client {
// Creates a new instance of our factory
pub fn new() -> Client {
glib::Object::new(Self::static_type(), &[])
.expect("Failed to create client")
.downcast()
.expect("Created client is of wrong type")
}
}
}
fn example_main() -> Result<(), Error> {
gst::init()?;
main_loop()
}
fn main() {
match examples_common::run(example_main) {
Ok(r) => r,
Err(e) => eprintln!("Error! {}", e),
}
}

View file

@ -385,7 +385,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -109,7 +109,7 @@ fn example_main() -> Result<(), Error> {
.unwrap_or_else(|| "None".into())
.to_string(),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -175,7 +175,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

2
gir

@ -1 +1 @@
Subproject commit 58365737247ee2b409c9ccfe689654aa987413d5
Subproject commit 2abca1147143e5f5986f14bf6bc794e438bef193

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-app"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer App library"
@ -13,19 +13,26 @@ keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"]
build = "build.rs"
[dependencies]
futures-core = "0.3"
futures-sink = "0.3"
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-app-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-app-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
lazy_static = "1.0"
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }
[dev-dependencies]
futures-util = { version = "0.3", features = ["sink"] }
futures-executor = "0.3"
[features]
default = []
v1_10 = ["gstreamer/v1_10", "gstreamer-base/v1_10", "gstreamer-app-sys/v1_10"]

View file

@ -12,14 +12,33 @@ use glib::signal::SignalHandlerId;
use glib::translate::*;
use glib_sys::gpointer;
use gst;
use gst::gst_element_error;
use gst_app_sys;
use gst_sys;
use std::boxed::Box as Box_;
use std::cell::RefCell;
use std::mem::transmute;
use std::panic;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use AppSink;
#[cfg(any(feature = "v1_10"))]
use {
futures_core::Stream,
glib::prelude::*,
std::{
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
},
};
lazy_static! {
static ref SET_ONCE_QUARK: glib::Quark =
glib::Quark::from_string("gstreamer-rs-app-sink-callbacks");
}
#[allow(clippy::type_complexity)]
pub struct AppSinkCallbacks {
eos: Option<RefCell<Box<dyn FnMut(&AppSink) + Send + 'static>>>,
@ -33,6 +52,7 @@ pub struct AppSinkCallbacks {
Box<dyn FnMut(&AppSink) -> Result<gst::FlowSuccess, gst::FlowError> + Send + 'static>,
>,
>,
panicked: AtomicBool,
callbacks: gst_app_sys::GstAppSinkCallbacks,
}
@ -107,6 +127,7 @@ impl AppSinkCallbacksBuilder {
eos: self.eos,
new_preroll: self.new_preroll,
new_sample: self.new_sample,
panicked: AtomicBool::new(false),
callbacks: gst_app_sys::GstAppSinkCallbacks {
eos: if have_eos { Some(trampoline_eos) } else { None },
new_preroll: if have_new_preroll {
@ -130,11 +151,37 @@ impl AppSinkCallbacksBuilder {
}
}
fn post_panic_error_message(element: &AppSink, err: &dyn std::any::Any) {
if let Some(cause) = err.downcast_ref::<&str>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else if let Some(cause) = err.downcast_ref::<String>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
}
}
unsafe extern "C" fn trampoline_eos(appsink: *mut gst_app_sys::GstAppSink, callbacks: gpointer) {
let callbacks = &*(callbacks as *const AppSinkCallbacks);
let element: AppSink = from_glib_borrow(appsink);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSink = from_glib_borrow(appsink);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return;
}
if let Some(ref eos) = callbacks.eos {
(&mut *eos.borrow_mut())(&from_glib_borrow(appsink))
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *eos.borrow_mut())(&element)
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
}
}
}
}
@ -143,9 +190,27 @@ unsafe extern "C" fn trampoline_new_preroll(
callbacks: gpointer,
) -> gst_sys::GstFlowReturn {
let callbacks = &*(callbacks as *const AppSinkCallbacks);
let element: AppSink = from_glib_borrow(appsink);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSink = from_glib_borrow(appsink);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return gst::FlowReturn::Error.to_glib();
}
let ret = if let Some(ref new_preroll) = callbacks.new_preroll {
(&mut *new_preroll.borrow_mut())(&from_glib_borrow(appsink)).into()
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *new_preroll.borrow_mut())(&element).into()
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
gst::FlowReturn::Error
}
}
} else {
gst::FlowReturn::Error
};
@ -158,9 +223,27 @@ unsafe extern "C" fn trampoline_new_sample(
callbacks: gpointer,
) -> gst_sys::GstFlowReturn {
let callbacks = &*(callbacks as *const AppSinkCallbacks);
let element: AppSink = from_glib_borrow(appsink);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSink = from_glib_borrow(appsink);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return gst::FlowReturn::Error.to_glib();
}
let ret = if let Some(ref new_sample) = callbacks.new_sample {
(&mut *new_sample.borrow_mut())(&from_glib_borrow(appsink)).into()
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *new_sample.borrow_mut())(&element).into()
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
gst::FlowReturn::Error
}
}
} else {
gst::FlowReturn::Error
};
@ -175,8 +258,26 @@ unsafe extern "C" fn destroy_callbacks(ptr: gpointer) {
impl AppSink {
pub fn set_callbacks(&self, callbacks: AppSinkCallbacks) {
unsafe {
let sink = self.to_glib_none().0;
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() < (1, 16, 3, 0) {
if !gobject_sys::g_object_get_qdata(sink as *mut _, SET_ONCE_QUARK.to_glib())
.is_null()
{
panic!("AppSink callbacks can only be set once");
}
gobject_sys::g_object_set_qdata(
sink as *mut _,
SET_ONCE_QUARK.to_glib(),
1 as *mut _,
);
}
gst_app_sys::gst_app_sink_set_callbacks(
self.to_glib_none().0,
sink,
mut_override(&callbacks.callbacks),
Box::into_raw(Box::new(callbacks)) as *mut _,
Some(destroy_callbacks),
@ -217,6 +318,11 @@ impl AppSink {
)
}
}
#[cfg(any(feature = "v1_10"))]
pub fn stream(&self) -> AppSinkStream {
AppSinkStream::new(self)
}
}
unsafe extern "C" fn new_sample_trampoline<
@ -240,3 +346,122 @@ unsafe extern "C" fn new_preroll_trampoline<
let ret: gst::FlowReturn = f(&from_glib_borrow(this)).into();
ret.to_glib()
}
#[cfg(any(feature = "v1_10"))]
#[derive(Debug)]
pub struct AppSinkStream {
app_sink: glib::WeakRef<AppSink>,
waker_reference: Arc<Mutex<Option<Waker>>>,
}
#[cfg(any(feature = "v1_10"))]
impl AppSinkStream {
fn new(app_sink: &AppSink) -> Self {
skip_assert_initialized!();
let waker_reference = Arc::new(Mutex::new(None as Option<Waker>));
app_sink.set_callbacks(
AppSinkCallbacks::new()
.new_sample({
let waker_reference = Arc::clone(&waker_reference);
move |_| {
if let Some(waker) = waker_reference.lock().unwrap().take() {
waker.wake();
}
Ok(gst::FlowSuccess::Ok)
}
})
.eos({
let waker_reference = Arc::clone(&waker_reference);
move |_| {
if let Some(waker) = waker_reference.lock().unwrap().take() {
waker.wake();
}
}
})
.build(),
);
Self {
app_sink: app_sink.downgrade(),
waker_reference,
}
}
}
#[cfg(any(feature = "v1_10"))]
impl Drop for AppSinkStream {
fn drop(&mut self) {
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() >= (1, 16, 3, 0) {
if let Some(app_sink) = self.app_sink.upgrade() {
app_sink.set_callbacks(AppSinkCallbacks::new().build());
}
}
}
}
#[cfg(any(feature = "v1_10"))]
impl Stream for AppSinkStream {
type Item = gst::Sample;
fn poll_next(self: Pin<&mut Self>, context: &mut Context) -> Poll<Option<Self::Item>> {
let mut waker = self.waker_reference.lock().unwrap();
let app_sink = match self.app_sink.upgrade() {
Some(app_sink) => app_sink,
None => return Poll::Ready(None),
};
app_sink
.try_pull_sample(gst::ClockTime::from_mseconds(0))
.map(|sample| Poll::Ready(Some(sample)))
.unwrap_or_else(|| {
if app_sink.is_eos() {
return Poll::Ready(None);
}
waker.replace(context.waker().to_owned());
Poll::Pending
})
}
}
#[cfg(any(feature = "v1_10"))]
#[cfg(test)]
mod tests {
use super::*;
use futures_util::StreamExt;
use gst::prelude::*;
#[test]
fn test_app_sink_stream() {
gst::init().unwrap();
let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap();
let appsink = gst::ElementFactory::make("appsink", None).unwrap();
videotestsrc.set_property("num-buffers", &5).unwrap();
let pipeline = gst::Pipeline::new(None);
pipeline.add(&videotestsrc).unwrap();
pipeline.add(&appsink).unwrap();
videotestsrc.link(&appsink).unwrap();
let app_sink_stream = appsink.dynamic_cast::<AppSink>().unwrap().stream();
let samples_future = app_sink_stream.collect::<Vec<gst::Sample>>();
pipeline.set_state(gst::State::Playing).unwrap();
let samples = futures_executor::block_on(samples_future);
pipeline.set_state(gst::State::Null).unwrap();
assert_eq!(samples.len(), 5);
}
}

View file

@ -6,20 +6,35 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use futures_sink::Sink;
use glib::prelude::*;
use glib::translate::*;
use glib_sys::{gboolean, gpointer};
use gst;
use gst::gst_element_error;
use gst_app_sys;
use std::cell::RefCell;
use std::mem;
use std::panic;
use std::pin::Pin;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
use AppSrc;
lazy_static! {
static ref SET_ONCE_QUARK: glib::Quark =
glib::Quark::from_string("gstreamer-rs-app-src-callbacks");
}
#[allow(clippy::type_complexity)]
pub struct AppSrcCallbacks {
need_data: Option<RefCell<Box<dyn FnMut(&AppSrc, u32) + Send + 'static>>>,
enough_data: Option<Box<dyn Fn(&AppSrc) + Send + Sync + 'static>>,
seek_data: Option<Box<dyn Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>>,
panicked: AtomicBool,
callbacks: gst_app_sys::GstAppSrcCallbacks,
}
@ -80,6 +95,7 @@ impl AppSrcCallbacksBuilder {
need_data: self.need_data,
enough_data: self.enough_data,
seek_data: self.seek_data,
panicked: AtomicBool::new(false),
callbacks: gst_app_sys::GstAppSrcCallbacks {
need_data: if have_need_data {
Some(trampoline_need_data)
@ -107,15 +123,41 @@ impl AppSrcCallbacksBuilder {
}
}
fn post_panic_error_message(element: &AppSrc, err: &dyn std::any::Any) {
if let Some(cause) = err.downcast_ref::<&str>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else if let Some(cause) = err.downcast_ref::<String>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
}
}
unsafe extern "C" fn trampoline_need_data(
appsrc: *mut gst_app_sys::GstAppSrc,
length: u32,
callbacks: gpointer,
) {
let callbacks = &*(callbacks as *const AppSrcCallbacks);
let element: AppSrc = from_glib_borrow(appsrc);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSrc = from_glib_borrow(appsrc);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return;
}
if let Some(ref need_data) = callbacks.need_data {
(&mut *need_data.borrow_mut())(&from_glib_borrow(appsrc), length);
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *need_data.borrow_mut())(&element, length)
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
}
}
}
}
@ -124,9 +166,23 @@ unsafe extern "C" fn trampoline_enough_data(
callbacks: gpointer,
) {
let callbacks = &*(callbacks as *const AppSrcCallbacks);
let element: AppSrc = from_glib_borrow(appsrc);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSrc = from_glib_borrow(appsrc);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return;
}
if let Some(ref enough_data) = callbacks.enough_data {
(*enough_data)(&from_glib_borrow(appsrc));
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| (*enough_data)(&element)));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
}
}
}
}
@ -136,9 +192,26 @@ unsafe extern "C" fn trampoline_seek_data(
callbacks: gpointer,
) -> gboolean {
let callbacks = &*(callbacks as *const AppSrcCallbacks);
let element: AppSrc = from_glib_borrow(appsrc);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSrc = from_glib_borrow(appsrc);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return false.to_glib();
}
let ret = if let Some(ref seek_data) = callbacks.seek_data {
(*seek_data)(&from_glib_borrow(appsrc), offset)
let result =
panic::catch_unwind(panic::AssertUnwindSafe(|| (*seek_data)(&element, offset)));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
false
}
}
} else {
false
};
@ -196,8 +269,25 @@ impl AppSrc {
pub fn set_callbacks(&self, callbacks: AppSrcCallbacks) {
unsafe {
let src = self.to_glib_none().0;
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() < (1, 16, 3, 0) {
if !gobject_sys::g_object_get_qdata(src as *mut _, SET_ONCE_QUARK.to_glib())
.is_null()
{
panic!("AppSrc callbacks can only be set once");
}
gobject_sys::g_object_set_qdata(
src as *mut _,
SET_ONCE_QUARK.to_glib(),
1 as *mut _,
);
}
gst_app_sys::gst_app_src_set_callbacks(
self.to_glib_none().0,
src,
mut_override(&callbacks.callbacks),
Box::into_raw(Box::new(callbacks)) as *mut _,
Some(destroy_callbacks),
@ -227,4 +317,172 @@ impl AppSrc {
(from_glib(min.assume_init()), from_glib(max.assume_init()))
}
}
pub fn sink(&self) -> AppSrcSink {
AppSrcSink::new(self)
}
}
#[derive(Debug)]
pub struct AppSrcSink {
app_src: glib::WeakRef<AppSrc>,
waker_reference: Arc<Mutex<Option<Waker>>>,
}
impl AppSrcSink {
fn new(app_src: &AppSrc) -> Self {
skip_assert_initialized!();
let waker_reference = Arc::new(Mutex::new(None as Option<Waker>));
app_src.set_callbacks(
AppSrcCallbacks::new()
.need_data({
let waker_reference = Arc::clone(&waker_reference);
move |_, _| {
if let Some(waker) = waker_reference.lock().unwrap().take() {
waker.wake();
}
}
})
.build(),
);
Self {
app_src: app_src.downgrade(),
waker_reference,
}
}
}
impl Drop for AppSrcSink {
fn drop(&mut self) {
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() >= (1, 16, 3, 0) {
if let Some(app_src) = self.app_src.upgrade() {
app_src.set_callbacks(AppSrcCallbacks::new().build());
}
}
}
}
impl Sink<gst::Sample> for AppSrcSink {
type Error = gst::FlowError;
fn poll_ready(self: Pin<&mut Self>, context: &mut Context) -> Poll<Result<(), Self::Error>> {
let mut waker = self.waker_reference.lock().unwrap();
let app_src = match self.app_src.upgrade() {
Some(app_src) => app_src,
None => return Poll::Ready(Err(gst::FlowError::Eos)),
};
let current_level_bytes = app_src.get_current_level_bytes();
let max_bytes = app_src.get_max_bytes();
if current_level_bytes >= max_bytes && max_bytes != 0 {
waker.replace(context.waker().to_owned());
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn start_send(self: Pin<&mut Self>, sample: gst::Sample) -> Result<(), Self::Error> {
let app_src = match self.app_src.upgrade() {
Some(app_src) => app_src,
None => return Err(gst::FlowError::Eos),
};
app_src.push_sample(&sample)?;
Ok(())
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
let app_src = match self.app_src.upgrade() {
Some(app_src) => app_src,
None => return Poll::Ready(Ok(())),
};
app_src.end_of_stream()?;
Poll::Ready(Ok(()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::{sink::SinkExt, stream::StreamExt};
use gst::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn test_app_src_sink() {
gst::init().unwrap();
let appsrc = gst::ElementFactory::make("appsrc", None).unwrap();
let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
fakesink.set_property("signal-handoffs", &true).unwrap();
let pipeline = gst::Pipeline::new(None);
pipeline.add(&appsrc).unwrap();
pipeline.add(&fakesink).unwrap();
appsrc.link(&fakesink).unwrap();
let mut bus_stream = pipeline.get_bus().unwrap().stream();
let mut app_src_sink = appsrc.dynamic_cast::<AppSrc>().unwrap().sink();
let sample_quantity = 5;
let samples = (0..sample_quantity)
.map(|_| gst::Sample::new().buffer(&gst::Buffer::new()).build())
.collect::<Vec<gst::Sample>>();
let mut sample_stream = futures_util::stream::iter(samples).map(Ok);
let handoff_count_reference = Arc::new(AtomicUsize::new(0));
fakesink
.connect("handoff", false, {
let handoff_count_reference = Arc::clone(&handoff_count_reference);
move |_| {
handoff_count_reference.fetch_add(1, Ordering::AcqRel);
None
}
})
.unwrap();
pipeline.set_state(gst::State::Playing).unwrap();
futures_executor::block_on(app_src_sink.send_all(&mut sample_stream)).unwrap();
futures_executor::block_on(app_src_sink.close()).unwrap();
while let Some(message) = futures_executor::block_on(bus_stream.next()) {
match message.view() {
gst::MessageView::Eos(_) => break,
gst::MessageView::Error(_) => unreachable!(),
_ => continue,
}
}
pipeline.set_state(gst::State::Null).unwrap();
assert_eq!(
handoff_count_reference.load(Ordering::Acquire),
sample_quantity
);
}
}

View file

@ -8,6 +8,8 @@
extern crate libc;
extern crate futures_core;
extern crate futures_sink;
extern crate glib_sys;
extern crate gobject_sys;
extern crate gstreamer as gst;
@ -15,9 +17,18 @@ extern crate gstreamer_app_sys as gst_app_sys;
extern crate gstreamer_base as gst_base;
extern crate gstreamer_sys as gst_sys;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate glib;
#[cfg(test)]
extern crate futures_util;
#[cfg(test)]
extern crate futures_executor;
macro_rules! skip_assert_initialized {
() => {};
}
@ -28,10 +39,11 @@ macro_rules! skip_assert_initialized {
mod auto;
pub use auto::*;
mod app_sink;
mod app_src;
pub use app_sink::*;
pub use app_src::*;
pub mod app_sink;
pub use app_sink::AppSinkCallbacks;
pub mod app_src;
pub use app_src::AppSrcCallbacks;
// Re-export all the traits in a prelude module, so that applications
// can always "use gst::prelude::*" without getting conflicts

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-audio"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Audio library"
@ -15,14 +15,14 @@ build = "build.rs"
[dependencies]
libc = "0.2"
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-base-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-audio-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-base-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-audio-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
array-init = "0.1"
[build-dependencies]

View file

@ -0,0 +1,765 @@
// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst_audio_sys;
use gst_sys;
use glib;
use glib::translate::{from_glib, FromGlibPtrNone, ToGlibPtr};
use gst;
use gst::miniobject::MiniObject;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ops;
use std::ptr;
use std::slice;
pub enum Readable {}
pub enum Writable {}
pub struct AudioBuffer<T> {
// Has to be boxed because it contains self-references
audio_buffer: Box<gst_audio_sys::GstAudioBuffer>,
buffer: Option<gst::Buffer>,
info: ::AudioInfo,
phantom: PhantomData<T>,
}
unsafe impl<T> Send for AudioBuffer<T> {}
unsafe impl<T> Sync for AudioBuffer<T> {}
impl<T> fmt::Debug for AudioBuffer<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AudioBuffer")
.field("audio_buffer", &self.audio_buffer)
.field("buffer", &self.buffer)
.field("info", &self.info)
.field("phantom", &self.phantom)
.finish()
}
}
impl<T> AudioBuffer<T> {
pub fn info(&self) -> &::AudioInfo {
&self.info
}
pub fn into_buffer(mut self) -> gst::Buffer {
self.buffer.take().unwrap()
}
pub fn format(&self) -> ::AudioFormat {
self.info().format()
}
pub fn format_info(&self) -> ::AudioFormatInfo {
self.info().format_info()
}
pub fn channels(&self) -> u32 {
self.info().channels()
}
pub fn rate(&self) -> u32 {
self.info().rate()
}
pub fn layout(&self) -> ::AudioLayout {
self.info().layout()
}
pub fn width(&self) -> u32 {
self.info().width()
}
pub fn depth(&self) -> u32 {
self.info().depth()
}
pub fn sample_stride(&self) -> u32 {
self.info().width() / 8
}
pub fn bps(&self) -> u32 {
self.info().bps()
}
pub fn bpf(&self) -> u32 {
self.info().bpf()
}
pub fn n_samples(&self) -> usize {
self.audio_buffer.n_samples
}
pub fn n_planes(&self) -> u32 {
self.audio_buffer.n_planes as u32
}
pub fn plane_size(&self) -> usize {
(self.n_samples() as usize * self.sample_stride() as usize * self.channels() as usize)
/ self.n_planes() as usize
}
pub fn buffer(&self) -> &gst::BufferRef {
unsafe { gst::BufferRef::from_ptr(self.audio_buffer.buffer) }
}
pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> {
if plane >= self.n_planes() {
return Err(glib_bool_error!("Plane index higher than number of planes"));
}
unsafe {
Ok(slice::from_raw_parts(
self.audio_buffer.planes.add(plane as usize) as *const u8,
self.plane_size(),
))
}
}
pub fn as_audio_buffer_ref(&self) -> AudioBufferRef<&gst::BufferRef> {
let info = self.info.clone();
AudioBufferRef {
audio_buffer: AudioBufferPtr::Borrowed(ptr::NonNull::from(&*self.audio_buffer)),
buffer: Some(self.buffer()),
info,
unmap: false,
}
}
pub fn as_ptr(&self) -> *const gst_audio_sys::GstAudioBuffer {
&*self.audio_buffer
}
}
impl<T> Drop for AudioBuffer<T> {
fn drop(&mut self) {
unsafe {
gst_audio_sys::gst_audio_buffer_unmap(&mut *self.audio_buffer);
}
}
}
impl AudioBuffer<Readable> {
pub fn from_buffer_readable(
buffer: gst::Buffer,
info: &::AudioInfo,
) -> Result<AudioBuffer<Readable>, gst::Buffer> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut audio_buffer = Box::new(mem::MaybeUninit::zeroed().assume_init());
let res: bool = from_glib(gst_audio_sys::gst_audio_buffer_map(
&mut *audio_buffer,
info.to_glib_none().0 as *mut _,
buffer.to_glib_none().0,
gst_sys::GST_MAP_READ,
));
if !res {
Err(buffer)
} else {
let info = ::AudioInfo::from_glib_none(
&audio_buffer.info as *const _ as *mut gst_audio_sys::GstAudioInfo,
);
Ok(AudioBuffer {
audio_buffer,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
}
impl AudioBuffer<Writable> {
pub fn from_buffer_writable(
buffer: gst::Buffer,
info: &::AudioInfo,
) -> Result<AudioBuffer<Writable>, gst::Buffer> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut audio_buffer = Box::new(mem::MaybeUninit::zeroed().assume_init());
let res: bool = from_glib(gst_audio_sys::gst_audio_buffer_map(
&mut *audio_buffer,
info.to_glib_none().0 as *mut _,
buffer.to_glib_none().0,
gst_sys::GST_MAP_READ | gst_sys::GST_MAP_WRITE,
));
if !res {
Err(buffer)
} else {
let info = ::AudioInfo::from_glib_none(
&audio_buffer.info as *const _ as *mut gst_audio_sys::GstAudioInfo,
);
Ok(AudioBuffer {
audio_buffer,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
unsafe { gst::BufferRef::from_mut_ptr(self.audio_buffer.buffer) }
}
pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> {
if plane >= self.n_planes() {
return Err(glib_bool_error!("Plane index higher than number of planes"));
}
unsafe {
Ok(slice::from_raw_parts_mut(
self.audio_buffer.planes.add(plane as usize) as *mut u8,
self.plane_size(),
))
}
}
pub fn as_mut_audio_buffer_ref(&mut self) -> AudioBufferRef<&mut gst::BufferRef> {
let info = self.info.clone();
AudioBufferRef {
audio_buffer: AudioBufferPtr::Borrowed(ptr::NonNull::from(&mut *self.audio_buffer)),
buffer: Some(self.buffer_mut()),
info,
unmap: false,
}
}
pub fn as_mut_ptr(&mut self) -> *mut gst_audio_sys::GstAudioBuffer {
&mut *self.audio_buffer
}
}
#[derive(Debug)]
enum AudioBufferPtr {
Owned(Box<gst_audio_sys::GstAudioBuffer>),
Borrowed(ptr::NonNull<gst_audio_sys::GstAudioBuffer>),
}
impl ops::Deref for AudioBufferPtr {
type Target = gst_audio_sys::GstAudioBuffer;
fn deref(&self) -> &Self::Target {
match self {
AudioBufferPtr::Owned(ref b) => &*b,
AudioBufferPtr::Borrowed(ref b) => unsafe { b.as_ref() },
}
}
}
impl ops::DerefMut for AudioBufferPtr {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
AudioBufferPtr::Owned(ref mut b) => &mut *b,
AudioBufferPtr::Borrowed(ref mut b) => unsafe { b.as_mut() },
}
}
}
#[derive(Debug)]
pub struct AudioBufferRef<T> {
// Has to be boxed because it contains self-references
audio_buffer: AudioBufferPtr,
buffer: Option<T>,
info: ::AudioInfo,
unmap: bool,
}
impl<T> AudioBufferRef<T> {
pub fn info(&self) -> &::AudioInfo {
&self.info
}
pub fn format(&self) -> ::AudioFormat {
self.info().format()
}
pub fn format_info(&self) -> ::AudioFormatInfo {
self.info().format_info()
}
pub fn channels(&self) -> u32 {
self.info().channels()
}
pub fn rate(&self) -> u32 {
self.info().rate()
}
pub fn layout(&self) -> ::AudioLayout {
self.info().layout()
}
pub fn width(&self) -> u32 {
self.info().width()
}
pub fn depth(&self) -> u32 {
self.info().depth()
}
pub fn sample_stride(&self) -> u32 {
self.info().width() / 8
}
pub fn bps(&self) -> u32 {
self.info().bps()
}
pub fn bpf(&self) -> u32 {
self.info().bpf()
}
pub fn n_samples(&self) -> usize {
self.audio_buffer.n_samples
}
pub fn n_planes(&self) -> u32 {
self.audio_buffer.n_planes as u32
}
pub fn plane_size(&self) -> usize {
(self.n_samples() as usize * self.sample_stride() as usize * self.channels() as usize)
/ self.n_planes() as usize
}
pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> {
if plane >= self.n_planes() {
return Err(glib_bool_error!("Plane index higher than number of planes"));
}
unsafe {
Ok(slice::from_raw_parts(
self.audio_buffer.planes.add(plane as usize) as *const u8,
self.plane_size(),
))
}
}
pub fn as_ptr(&self) -> *const gst_audio_sys::GstAudioBuffer {
&*self.audio_buffer
}
}
impl<'a> AudioBufferRef<&'a gst::BufferRef> {
pub unsafe fn from_glib_borrow(audio_buffer: *const gst_audio_sys::GstAudioBuffer) -> Self {
assert!(!audio_buffer.is_null());
let info = ::AudioInfo::from_glib_none(
&(*audio_buffer).info as *const _ as *mut gst_audio_sys::GstAudioInfo,
);
let buffer = gst::BufferRef::from_ptr((*audio_buffer).buffer);
AudioBufferRef {
audio_buffer: AudioBufferPtr::Borrowed(ptr::NonNull::new_unchecked(
audio_buffer as *mut _,
)),
buffer: Some(buffer),
info,
unmap: false,
}
}
pub fn from_buffer_ref_readable<'b>(
buffer: &'a gst::BufferRef,
info: &'b ::AudioInfo,
) -> Result<AudioBufferRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut audio_buffer = Box::new(mem::MaybeUninit::zeroed().assume_init());
let res: bool = from_glib(gst_audio_sys::gst_audio_buffer_map(
&mut *audio_buffer,
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map AudioBuffer"))
} else {
let info = ::AudioInfo::from_glib_none(
&audio_buffer.info as *const _ as *mut gst_audio_sys::GstAudioInfo,
);
Ok(AudioBufferRef {
audio_buffer: AudioBufferPtr::Owned(audio_buffer),
buffer: Some(buffer),
info,
unmap: true,
})
}
}
}
pub fn buffer(&self) -> &gst::BufferRef {
self.buffer.as_ref().unwrap()
}
}
impl<'a> AudioBufferRef<&'a mut gst::BufferRef> {
pub unsafe fn from_glib_borrow_mut(audio_buffer: *mut gst_audio_sys::GstAudioBuffer) -> Self {
assert!(!audio_buffer.is_null());
let info = ::AudioInfo::from_glib_none(
&(*audio_buffer).info as *const _ as *mut gst_audio_sys::GstAudioInfo,
);
let buffer = gst::BufferRef::from_mut_ptr((*audio_buffer).buffer);
AudioBufferRef {
audio_buffer: AudioBufferPtr::Borrowed(ptr::NonNull::new_unchecked(audio_buffer)),
buffer: Some(buffer),
info,
unmap: false,
}
}
pub fn from_buffer_ref_writable<'b>(
buffer: &'a mut gst::BufferRef,
info: &'b ::AudioInfo,
) -> Result<AudioBufferRef<&'a mut gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut audio_buffer = Box::new(mem::MaybeUninit::zeroed().assume_init());
let res: bool = from_glib(gst_audio_sys::gst_audio_buffer_map(
&mut *audio_buffer,
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
gst_sys::GST_MAP_READ | gst_sys::GST_MAP_WRITE,
));
if !res {
Err(glib_bool_error!("Failed to map AudioBuffer"))
} else {
let info = ::AudioInfo::from_glib_none(
&audio_buffer.info as *const _ as *mut gst_audio_sys::GstAudioInfo,
);
Ok(AudioBufferRef {
audio_buffer: AudioBufferPtr::Owned(audio_buffer),
buffer: Some(buffer),
info,
unmap: true,
})
}
}
}
pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
self.buffer.as_mut().unwrap()
}
pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> {
if plane >= self.n_planes() {
return Err(glib_bool_error!("Plane index higher than number of planes"));
}
unsafe {
Ok(slice::from_raw_parts_mut(
self.audio_buffer.planes.add(plane as usize) as *mut u8,
self.plane_size(),
))
}
}
pub fn as_mut_ptr(&mut self) -> *mut gst_audio_sys::GstAudioBuffer {
&mut *self.audio_buffer
}
}
impl<'a> ops::Deref for AudioBufferRef<&'a mut gst::BufferRef> {
type Target = AudioBufferRef<&'a gst::BufferRef>;
fn deref(&self) -> &Self::Target {
unsafe {
&*(self as *const AudioBufferRef<&'a mut gst::BufferRef>
as *const AudioBufferRef<&'a gst::BufferRef>)
}
}
}
unsafe impl<T> Send for AudioBufferRef<T> {}
unsafe impl<T> Sync for AudioBufferRef<T> {}
impl<T> Drop for AudioBufferRef<T> {
fn drop(&mut self) {
unsafe {
if self.unmap {
gst_audio_sys::gst_audio_buffer_unmap(&mut *self.audio_buffer);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use gst;
#[test]
fn test_map_read() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.build()
.unwrap();
let buffer = gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
let buffer = AudioBuffer::from_buffer_readable(buffer, &info).unwrap();
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data(1).is_err());
assert!(buffer.info() == &info);
{
let buffer = buffer.as_audio_buffer_ref();
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data(1).is_err());
assert!(buffer.info() == &info);
}
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data(1).is_err());
assert!(buffer.info() == &info);
}
#[test]
fn test_map_read_planar() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.layout(::AudioLayout::NonInterleaved)
.build()
.unwrap();
let mut buffer =
gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
{
let buffer = buffer.get_mut().unwrap();
::AudioMeta::add(buffer, &info, 48000, &[]).unwrap();
}
let buffer = AudioBuffer::from_buffer_readable(buffer, &info).unwrap();
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data(1).is_ok());
assert_eq!(buffer.plane_data(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
{
let buffer = buffer.as_audio_buffer_ref();
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data(1).is_ok());
assert_eq!(buffer.plane_data(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
}
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data(1).is_ok());
assert_eq!(buffer.plane_data(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
}
#[test]
fn test_map_write() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.build()
.unwrap();
let buffer = gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
let mut buffer = AudioBuffer::from_buffer_writable(buffer, &info).unwrap();
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data_mut(1).is_err());
assert!(buffer.info() == &info);
{
let mut buffer = buffer.as_mut_audio_buffer_ref();
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data_mut(1).is_err());
assert!(buffer.info() == &info);
}
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data_mut(1).is_err());
assert!(buffer.info() == &info);
}
#[test]
fn test_map_write_planar() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.layout(::AudioLayout::NonInterleaved)
.build()
.unwrap();
let mut buffer =
gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
{
let buffer = buffer.get_mut().unwrap();
::AudioMeta::add(buffer, &info, 48000, &[]).unwrap();
}
let mut buffer = AudioBuffer::from_buffer_writable(buffer, &info).unwrap();
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data_mut(1).is_ok());
assert_eq!(buffer.plane_data_mut(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
{
let mut buffer = buffer.as_mut_audio_buffer_ref();
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data_mut(1).is_ok());
assert_eq!(buffer.plane_data_mut(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
}
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data_mut(1).is_ok());
assert_eq!(buffer.plane_data_mut(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
}
#[test]
fn test_map_ref_read() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.build()
.unwrap();
let buffer = gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
let buffer = AudioBufferRef::from_buffer_ref_readable(&buffer, &info).unwrap();
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data(1).is_err());
assert!(buffer.info() == &info);
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data(1).is_err());
assert!(buffer.info() == &info);
}
#[test]
fn test_map_ref_read_planar() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.layout(::AudioLayout::NonInterleaved)
.build()
.unwrap();
let mut buffer =
gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
{
let buffer = buffer.get_mut().unwrap();
::AudioMeta::add(buffer, &info, 48000, &[]).unwrap();
}
let buffer = AudioBufferRef::from_buffer_ref_readable(&buffer, &info).unwrap();
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data(1).is_ok());
assert_eq!(buffer.plane_data(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
assert!(buffer.plane_data(0).is_ok());
assert_eq!(buffer.plane_data(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data(1).is_ok());
assert_eq!(buffer.plane_data(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
}
#[test]
fn test_map_ref_write() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.build()
.unwrap();
let mut buffer =
gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
{
let buffer = buffer.get_mut().unwrap();
let mut buffer = AudioBufferRef::from_buffer_ref_writable(buffer, &info).unwrap();
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data_mut(1).is_err());
assert!(buffer.info() == &info);
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 2 * 48000);
assert!(buffer.plane_data_mut(1).is_err());
assert!(buffer.info() == &info);
}
}
#[test]
fn test_map_ref_write_planar() {
gst::init().unwrap();
let info = ::AudioInfo::new(::AUDIO_FORMAT_S16, 48000, 2)
.layout(::AudioLayout::NonInterleaved)
.build()
.unwrap();
let mut buffer =
gst::Buffer::with_size(info.rate() as usize * info.bpf() as usize).unwrap();
{
let buffer = buffer.get_mut().unwrap();
::AudioMeta::add(buffer, &info, 48000, &[]).unwrap();
}
{
let buffer = buffer.get_mut().unwrap();
let mut buffer = AudioBufferRef::from_buffer_ref_writable(buffer, &info).unwrap();
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data_mut(1).is_ok());
assert_eq!(buffer.plane_data_mut(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
assert!(buffer.plane_data_mut(0).is_ok());
assert_eq!(buffer.plane_data_mut(0).unwrap().len(), 2 * 48000);
assert!(buffer.plane_data_mut(1).is_ok());
assert_eq!(buffer.plane_data_mut(1).unwrap().len(), 2 * 48000);
assert!(buffer.info() == &info);
}
}
}

View file

@ -151,6 +151,10 @@ impl AudioInfo {
}
}
pub fn is_valid(&self) -> bool {
!self.0.finfo.is_null() && self.0.channels > 0 && self.0.rate > 0 && self.0.bpf > 0
}
pub fn from_caps(caps: &gst::CapsRef) -> Result<AudioInfo, glib::error::BoolError> {
skip_assert_initialized!();
@ -230,6 +234,10 @@ impl AudioInfo {
}
pub fn format(&self) -> ::AudioFormat {
if self.0.finfo.is_null() {
return ::AudioFormat::Unknown;
}
unsafe { from_glib((*self.0.finfo).format) }
}

View file

@ -0,0 +1,251 @@
// Copyright (C) 2018-2020 Sebastian Dröge <sebastian@centricular.com>
// Copyright (C) 2020 Andrew Eikum <aeikum@codeweavers.com> for CodeWeavers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt;
#[cfg(any(feature = "v1_16", feature = "dox"))]
use std::ptr;
#[cfg(any(feature = "v1_16", feature = "dox"))]
use std::slice;
use glib;
use glib::translate::{from_glib, ToGlib};
#[cfg(any(feature = "v1_16", feature = "dox"))]
use glib::translate::{from_glib_none, ToGlibPtr};
use gst;
use gst::prelude::*;
use gst_audio_sys;
#[repr(C)]
pub struct AudioClippingMeta(gst_audio_sys::GstAudioClippingMeta);
unsafe impl Send for AudioClippingMeta {}
unsafe impl Sync for AudioClippingMeta {}
impl AudioClippingMeta {
pub fn add<V: Into<gst::GenericFormattedValue>>(
buffer: &mut gst::BufferRef,
start: V,
end: V,
) -> gst::MetaRefMut<Self, gst::meta::Standalone> {
let start = start.into();
let end = end.into();
assert_eq!(start.get_format(), end.get_format());
unsafe {
let meta = gst_audio_sys::gst_buffer_add_audio_clipping_meta(
buffer.as_mut_ptr(),
start.get_format().to_glib(),
start.get_value() as u64,
end.get_value() as u64,
);
Self::from_mut_ptr(buffer, meta)
}
}
pub fn get_start(&self) -> gst::GenericFormattedValue {
gst::GenericFormattedValue::new(from_glib(self.0.format), self.0.start as i64)
}
pub fn get_end(&self) -> gst::GenericFormattedValue {
gst::GenericFormattedValue::new(from_glib(self.0.format), self.0.end as i64)
}
}
unsafe impl MetaAPI for AudioClippingMeta {
type GstType = gst_audio_sys::GstAudioClippingMeta;
fn get_meta_api() -> glib::Type {
unsafe { from_glib(gst_audio_sys::gst_audio_clipping_meta_api_get_type()) }
}
}
impl fmt::Debug for AudioClippingMeta {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AudioClippingMeta")
.field("start", &self.get_start())
.field("end", &self.get_end())
.finish()
}
}
#[cfg(any(feature = "v1_16", feature = "dox"))]
#[repr(C)]
pub struct AudioMeta(gst_audio_sys::GstAudioMeta);
#[cfg(any(feature = "v1_16", feature = "dox"))]
unsafe impl Send for AudioMeta {}
#[cfg(any(feature = "v1_16", feature = "dox"))]
unsafe impl Sync for AudioMeta {}
#[cfg(any(feature = "v1_16", feature = "dox"))]
impl AudioMeta {
pub fn add<'a>(
buffer: &'a mut gst::BufferRef,
info: &::AudioInfo,
samples: usize,
offsets: &[usize],
) -> Result<gst::MetaRefMut<'a, Self, gst::meta::Standalone>, glib::BoolError> {
skip_assert_initialized!();
if !info.is_valid() {
return Err(glib_bool_error!("Invalid audio info"));
}
if info.rate() == 0
|| info.channels() == 0
|| info.format() == ::AudioFormat::Unknown
|| info.format() == ::AudioFormat::Encoded
{
return Err(glib_bool_error!("Unsupported audio format {:?}", info));
}
if !offsets.is_empty() && info.layout() != ::AudioLayout::NonInterleaved {
return Err(glib_bool_error!(
"Channel offsets only supported for non-interleaved audio"
));
}
if !offsets.is_empty() && offsets.len() != info.channels() as usize {
return Err(glib_bool_error!(
"Number of channel offsets different than number of channels ({} != {})",
offsets.len(),
info.channels()
));
}
if info.layout() == ::AudioLayout::NonInterleaved {
let plane_size = samples * (info.width() / 8) as usize;
let max_offset = if offsets.is_empty() {
plane_size * (info.channels() - 1) as usize
} else {
let mut max_offset = None;
for (i, offset) in offsets.iter().copied().enumerate() {
if let Some(current_max_offset) = max_offset {
max_offset = Some(std::cmp::max(current_max_offset, offset));
} else {
max_offset = Some(offset);
}
for (j, other_offset) in offsets.iter().copied().enumerate() {
if i != j
&& !(other_offset + plane_size <= offset
|| offset + plane_size <= other_offset)
{
return Err(glib_bool_error!("Overlapping audio channel offsets: offset {} for channel {} and offset {} for channel {} with a plane size of {}", offset, i, other_offset, j, plane_size));
}
}
}
max_offset.unwrap()
};
if max_offset + plane_size > buffer.get_size() {
return Err(glib_bool_error!("Audio channel offsets out of bounds: max offset {} with plane size {} and buffer size {}", max_offset, plane_size, buffer.get_size()));
}
}
unsafe {
let meta = gst_audio_sys::gst_buffer_add_audio_meta(
buffer.as_mut_ptr(),
info.to_glib_none().0,
samples,
if offsets.is_empty() {
ptr::null_mut()
} else {
offsets.as_ptr() as *mut _
},
);
if meta.is_null() {
return Err(glib_bool_error!("Failed to add audio meta"));
}
Ok(Self::from_mut_ptr(buffer, meta))
}
}
pub fn get_info(&self) -> ::AudioInfo {
unsafe { from_glib_none(&self.0.info as *const _ as *mut gst_audio_sys::GstAudioInfo) }
}
pub fn get_samples(&self) -> usize {
self.0.samples
}
pub fn get_offsets(&self) -> &[usize] {
if self.0.offsets.is_null() || self.0.info.channels < 1 {
return &[];
}
unsafe { slice::from_raw_parts(self.0.offsets, self.0.info.channels as usize) }
}
}
#[cfg(any(feature = "v1_16", feature = "dox"))]
unsafe impl MetaAPI for AudioMeta {
type GstType = gst_audio_sys::GstAudioMeta;
fn get_meta_api() -> glib::Type {
unsafe { from_glib(gst_audio_sys::gst_audio_meta_api_get_type()) }
}
}
#[cfg(any(feature = "v1_16", feature = "dox"))]
impl fmt::Debug for AudioMeta {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("AudioMeta")
.field("info", &self.get_info())
.field("samples", &self.get_samples())
.field("offsets", &self.get_offsets())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_get_meta() {
use std::convert::TryInto;
gst::init().unwrap();
let mut buffer = gst::Buffer::with_size(1024).unwrap();
{
let cmeta = AudioClippingMeta::add(
buffer.get_mut().unwrap(),
gst::format::Default(Some(1)),
gst::format::Default(Some(2)),
);
assert_eq!(
cmeta.get_start().try_into(),
Ok(gst::format::Default(Some(1)))
);
assert_eq!(
cmeta.get_end().try_into(),
Ok(gst::format::Default(Some(2)))
);
}
{
let cmeta = buffer.get_meta::<AudioClippingMeta>().unwrap();
assert_eq!(
cmeta.get_start().try_into(),
Ok(gst::format::Default(Some(1)))
);
assert_eq!(
cmeta.get_end().try_into(),
Ok(gst::format::Default(Some(2)))
);
}
}
}

View file

@ -37,46 +37,48 @@ impl AudioStreamAlign {
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_alignment_threshold(&mut self) -> gst::ClockTime {
pub fn get_alignment_threshold(&self) -> gst::ClockTime {
unsafe {
from_glib(
gst_audio_sys::gst_audio_stream_align_get_alignment_threshold(
self.to_glib_none_mut().0,
),
gst_audio_sys::gst_audio_stream_align_get_alignment_threshold(mut_override(
self.to_glib_none().0,
)),
)
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_discont_wait(&mut self) -> gst::ClockTime {
pub fn get_discont_wait(&self) -> gst::ClockTime {
unsafe {
from_glib(gst_audio_sys::gst_audio_stream_align_get_discont_wait(
self.to_glib_none_mut().0,
mut_override(self.to_glib_none().0),
))
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_rate(&mut self) -> i32 {
unsafe { gst_audio_sys::gst_audio_stream_align_get_rate(self.to_glib_none_mut().0) }
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_samples_since_discont(&mut self) -> u64 {
pub fn get_rate(&self) -> i32 {
unsafe {
gst_audio_sys::gst_audio_stream_align_get_samples_since_discont(
self.to_glib_none_mut().0,
)
gst_audio_sys::gst_audio_stream_align_get_rate(mut_override(self.to_glib_none().0))
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_timestamp_at_discont(&mut self) -> gst::ClockTime {
pub fn get_samples_since_discont(&self) -> u64 {
unsafe {
gst_audio_sys::gst_audio_stream_align_get_samples_since_discont(mut_override(
self.to_glib_none().0,
))
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_timestamp_at_discont(&self) -> gst::ClockTime {
unsafe {
from_glib(
gst_audio_sys::gst_audio_stream_align_get_timestamp_at_discont(
self.to_glib_none_mut().0,
),
gst_audio_sys::gst_audio_stream_align_get_timestamp_at_discont(mut_override(
self.to_glib_none().0,
)),
)
}
}

View file

@ -0,0 +1,47 @@
// Copyright (C) 2017-2020 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use glib::translate::{from_glib_full, ToGlibPtr};
use gst;
pub fn audio_buffer_clip(
buffer: gst::Buffer,
segment: &gst::Segment,
rate: u32,
bpf: u32,
) -> Option<gst::Buffer> {
skip_assert_initialized!();
unsafe {
from_glib_full(gst_audio_sys::gst_audio_buffer_clip(
buffer.into_ptr(),
segment.to_glib_none().0,
rate as i32,
bpf as i32,
))
}
}
#[cfg(any(feature = "v1_16", feature = "dox"))]
pub fn audio_buffer_truncate(
buffer: gst::Buffer,
bpf: u32,
trim: usize,
samples: Option<usize>,
) -> gst::Buffer {
skip_assert_initialized!();
unsafe {
from_glib_full(gst_audio_sys::gst_audio_buffer_truncate(
buffer.into_ptr(),
bpf as i32,
trim,
samples.unwrap_or(std::usize::MAX),
))
}
}

View file

@ -47,35 +47,24 @@ mod audio_ring_buffer_spec;
pub use audio_ring_buffer_spec::*;
mod audio_info;
pub use audio_info::*;
mod audio_meta;
pub use audio_meta::*;
mod audio_channel_position;
pub use audio_channel_position::*;
#[cfg(any(feature = "v1_14", feature = "dox"))]
mod audio_stream_align;
mod functions;
pub use functions::*;
#[cfg(any(feature = "v1_16", feature = "dox"))]
pub mod audio_buffer;
#[cfg(any(feature = "v1_16", feature = "dox"))]
pub use audio_buffer::{AudioBuffer, AudioBufferRef};
mod audio_decoder;
pub use audio_decoder::AudioDecoderExtManual;
mod audio_encoder;
pub use audio_encoder::AudioEncoderExtManual;
use glib::translate::{from_glib_full, ToGlibPtr};
pub fn audio_buffer_clip(
buffer: gst::Buffer,
segment: &gst::Segment,
rate: u32,
bpf: u32,
) -> Option<gst::Buffer> {
skip_assert_initialized!();
unsafe {
from_glib_full(gst_audio_sys::gst_audio_buffer_clip(
buffer.into_ptr(),
segment.to_glib_none().0,
rate as i32,
bpf as i32,
))
}
}
// Re-export all the traits in a prelude module, so that applications
// can always "use gst::prelude::*" without getting conflicts
pub mod prelude {

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-base"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Base library"
@ -15,12 +15,12 @@ build = "build.rs"
[dependencies]
libc = "0.2"
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-base-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-base-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -21,7 +21,7 @@ impl<O: IsA<AggregatorPad>> AggregatorPadExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let ptr: &gst_base_sys::GstAggregatorPad = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&ptr.parent.object.lock);
let _guard = ::utils::MutexGuard::lock(&ptr.parent.object.lock);
from_glib_none(&ptr.segment as *const gst_sys::GstSegment)
}
}

View file

@ -39,7 +39,7 @@ impl<O: IsA<BaseSink>> BaseSinkExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let sink: &gst_base_sys::GstBaseSink = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&sink.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&sink.element.object.lock);
from_glib_none(&sink.segment as *const _)
}
}

View file

@ -29,7 +29,7 @@ impl<O: IsA<BaseSrc>> BaseSrcExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let src: &gst_base_sys::GstBaseSrc = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&src.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&src.element.object.lock);
from_glib_none(&src.segment as *const _)
}
}

View file

@ -20,7 +20,7 @@ impl<O: IsA<BaseTransform>> BaseTransformExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let trans: &gst_base_sys::GstBaseTransform = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&trans.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&trans.element.object.lock);
from_glib_none(&trans.segment as *const _)
}
}

View file

@ -111,7 +111,7 @@ impl UniqueFlowCombiner {
self.0.add_pad(pad);
}
pub fn clear(&self) {
pub fn clear(&mut self) {
self.0.clear();
}

View file

@ -18,6 +18,7 @@ use gst;
use gst::subclass::prelude::*;
use std::mem;
use std::ptr;
use BaseTransform;
use BaseTransformClass;
@ -101,6 +102,14 @@ pub trait BaseTransformImpl: BaseTransformImplExt + ElementImpl + Send + Sync +
self.parent_src_event(element, event)
}
fn prepare_output_buffer(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
) -> Result<PreparedOutputBuffer, gst::FlowError> {
self.parent_prepare_output_buffer(element, inbuf)
}
fn transform(
&self,
element: &BaseTransform,
@ -125,6 +134,42 @@ pub trait BaseTransformImpl: BaseTransformImplExt + ElementImpl + Send + Sync +
) -> Result<gst::FlowSuccess, gst::FlowError> {
self.parent_transform_ip_passthrough(element, buf)
}
fn copy_metadata(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
outbuf: &mut gst::BufferRef,
) -> Result<(), gst::LoggableError> {
self.parent_copy_metadata(element, inbuf, outbuf)
}
fn transform_meta<'a>(
&self,
element: &BaseTransform,
outbuf: &mut gst::BufferRef,
meta: gst::MetaRef<'a, gst::Meta>,
inbuf: &'a gst::BufferRef,
) -> bool {
self.parent_transform_meta(element, outbuf, meta, inbuf)
}
fn before_transform(&self, element: &BaseTransform, inbuf: &gst::BufferRef) {
self.parent_before_transform(element, inbuf);
}
fn submit_input_buffer(
&self,
element: &BaseTransform,
is_discont: bool,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
self.parent_submit_input_buffer(element, is_discont, inbuf)
}
fn generate_output(&self, element: &BaseTransform) -> Result<GeneratedOutput, gst::FlowError> {
self.parent_generate_output(element)
}
}
pub trait BaseTransformImplExt {
@ -184,6 +229,12 @@ pub trait BaseTransformImplExt {
fn parent_src_event(&self, element: &BaseTransform, event: gst::Event) -> bool;
fn parent_prepare_output_buffer(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
) -> Result<PreparedOutputBuffer, gst::FlowError>;
fn parent_transform(
&self,
element: &BaseTransform,
@ -202,6 +253,45 @@ pub trait BaseTransformImplExt {
element: &BaseTransform,
buf: &gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError>;
fn parent_copy_metadata(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
outbuf: &mut gst::BufferRef,
) -> Result<(), gst::LoggableError>;
fn parent_transform_meta<'a>(
&self,
element: &BaseTransform,
outbuf: &mut gst::BufferRef,
meta: gst::MetaRef<'a, gst::Meta>,
inbuf: &'a gst::BufferRef,
) -> bool;
fn parent_before_transform(&self, element: &BaseTransform, inbuf: &gst::BufferRef);
fn parent_submit_input_buffer(
&self,
element: &BaseTransform,
is_discont: bool,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError>;
fn parent_generate_output(
&self,
element: &BaseTransform,
) -> Result<GeneratedOutput, gst::FlowError>;
fn take_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>;
fn get_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>;
}
impl<T: BaseTransformImpl + ObjectImpl> BaseTransformImplExt for T {
@ -458,6 +548,41 @@ impl<T: BaseTransformImpl + ObjectImpl> BaseTransformImplExt for T {
}
}
fn parent_prepare_output_buffer(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
) -> Result<PreparedOutputBuffer, gst::FlowError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
(*parent_class)
.prepare_output_buffer
.map(|f| {
let mut outbuf: *mut gst_sys::GstBuffer = ptr::null_mut();
// FIXME: Wrong signature in FFI
let res = from_glib(f(
element.to_glib_none().0,
inbuf.as_ptr() as *mut gst_sys::GstBuffer,
(&mut outbuf) as *mut *mut gst_sys::GstBuffer as *mut gst_sys::GstBuffer,
));
match gst::FlowReturn::into_result(res) {
Err(err) => Err(err),
Ok(_) => {
if outbuf == inbuf.as_ptr() as *mut _ {
Ok(PreparedOutputBuffer::InputBuffer)
} else {
Ok(PreparedOutputBuffer::Buffer(from_glib_full(outbuf)))
}
}
}
})
.unwrap_or(Err(gst::FlowError::NotSupported))
}
}
fn parent_transform(
&self,
element: &BaseTransform,
@ -547,6 +672,149 @@ impl<T: BaseTransformImpl + ObjectImpl> BaseTransformImplExt for T {
gst::FlowReturn::from_glib(f(element.to_glib_none().0, buf as *mut _)).into_result()
}
}
fn parent_copy_metadata(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
outbuf: &mut gst::BufferRef,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
if let Some(ref f) = (*parent_class).copy_metadata {
gst_result_from_gboolean!(
f(
element.to_glib_none().0,
inbuf.as_ptr() as *mut _,
outbuf.as_mut_ptr()
),
gst::CAT_RUST,
"Parent function `copy_metadata` failed"
)
} else {
Ok(())
}
}
}
fn parent_transform_meta<'a>(
&self,
element: &BaseTransform,
outbuf: &mut gst::BufferRef,
meta: gst::MetaRef<'a, gst::Meta>,
inbuf: &'a gst::BufferRef,
) -> bool {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
(*parent_class)
.transform_meta
.map(|f| {
from_glib(f(
element.to_glib_none().0,
outbuf.as_mut_ptr(),
meta.as_ptr() as *mut _,
inbuf.as_ptr() as *mut _,
))
})
.unwrap_or(false)
}
}
fn parent_before_transform(&self, element: &BaseTransform, inbuf: &gst::BufferRef) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
if let Some(ref f) = (*parent_class).before_transform {
f(element.to_glib_none().0, inbuf.as_ptr() as *mut _);
}
}
}
fn parent_submit_input_buffer(
&self,
element: &BaseTransform,
is_discont: bool,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
let f = (*parent_class)
.submit_input_buffer
.expect("Missing parent function `submit_input_buffer`");
gst::FlowReturn::from_glib(f(
element.to_glib_none().0,
is_discont.to_glib(),
inbuf.into_ptr(),
))
.into_result()
}
}
fn parent_generate_output(
&self,
element: &BaseTransform,
) -> Result<GeneratedOutput, gst::FlowError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
let f = (*parent_class)
.generate_output
.expect("Missing parent function `generate_output`");
let mut outbuf = ptr::null_mut();
gst::FlowReturn::from_glib(f(element.to_glib_none().0, &mut outbuf))
.into_result()
.map(|res| {
if res == ::BASE_TRANSFORM_FLOW_DROPPED {
GeneratedOutput::Dropped
} else if res != gst::FlowSuccess::Ok || outbuf.is_null() {
GeneratedOutput::NoOutput
} else {
GeneratedOutput::Buffer(from_glib_full(outbuf))
}
})
}
}
fn take_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
{
unsafe {
let element = self.get_instance();
let ptr: *mut gst_base_sys::GstBaseTransform = element.to_glib_none().0 as *mut _;
let sinkpad: gst::Pad = from_glib_borrow((*ptr).sinkpad);
let _stream_lock = sinkpad.stream_lock();
let buffer = (*ptr).queued_buf;
(*ptr).queued_buf = ptr::null_mut();
from_glib_full(buffer)
}
}
fn get_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
{
unsafe {
let element = self.get_instance();
let ptr: *mut gst_base_sys::GstBaseTransform = element.to_glib_none().0 as *mut _;
let sinkpad: gst::Pad = from_glib_borrow((*ptr).sinkpad);
let _stream_lock = sinkpad.stream_lock();
let buffer = (*ptr).queued_buf;
from_glib_none(buffer)
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -573,8 +841,14 @@ where
klass.query = Some(base_transform_query::<T>);
klass.transform_size = Some(base_transform_transform_size::<T>);
klass.get_unit_size = Some(base_transform_get_unit_size::<T>);
klass.prepare_output_buffer = Some(base_transform_prepare_output_buffer::<T>);
klass.sink_event = Some(base_transform_sink_event::<T>);
klass.src_event = Some(base_transform_src_event::<T>);
klass.transform_meta = Some(base_transform_transform_meta::<T>);
klass.copy_metadata = Some(base_transform_copy_metadata::<T>);
klass.before_transform = Some(base_transform_before_transform::<T>);
klass.submit_input_buffer = Some(base_transform_submit_input_buffer::<T>);
klass.generate_output = Some(base_transform_generate_output::<T>);
}
}
}
@ -618,6 +892,19 @@ where
{
}
#[derive(Debug)]
pub enum GeneratedOutput {
Buffer(gst::Buffer),
NoOutput,
Dropped,
}
#[derive(Debug)]
pub enum PreparedOutputBuffer {
Buffer(gst::Buffer),
InputBuffer,
}
unsafe extern "C" fn base_transform_start<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
) -> glib_sys::gboolean
@ -848,6 +1135,38 @@ where
.to_glib()
}
unsafe extern "C" fn base_transform_prepare_output_buffer<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
inbuf: *mut gst_sys::GstBuffer,
outbuf: *mut gst_sys::GstBuffer,
) -> gst_sys::GstFlowReturn
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
// FIXME: Wrong signature in FFI
let outbuf = outbuf as *mut *mut gst_sys::GstBuffer;
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
match imp.prepare_output_buffer(&wrap, gst::BufferRef::from_ptr(inbuf)) {
Ok(PreparedOutputBuffer::InputBuffer) => {
*outbuf = inbuf;
gst::FlowReturn::Ok
}
Ok(PreparedOutputBuffer::Buffer(buf)) => {
*outbuf = buf.into_ptr();
gst::FlowReturn::Ok
}
Err(err) => err.into(),
}
})
.to_glib()
}
unsafe extern "C" fn base_transform_sink_event<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
event: *mut gst_sys::GstEvent,
@ -934,3 +1253,133 @@ where
})
.to_glib()
}
unsafe extern "C" fn base_transform_transform_meta<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
outbuf: *mut gst_sys::GstBuffer,
meta: *mut gst_sys::GstMeta,
inbuf: *mut gst_sys::GstBuffer,
) -> glib_sys::gboolean
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
let inbuf = gst::BufferRef::from_ptr(inbuf);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
imp.transform_meta(
&wrap,
gst::BufferRef::from_mut_ptr(outbuf),
gst::Meta::from_ptr(inbuf, meta),
inbuf,
)
})
.to_glib()
}
unsafe extern "C" fn base_transform_copy_metadata<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
inbuf: *mut gst_sys::GstBuffer,
outbuf: *mut gst_sys::GstBuffer,
) -> glib_sys::gboolean
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
if gst_sys::gst_mini_object_is_writable(outbuf as *mut _) == glib_sys::GFALSE {
gst_warning!(
gst::CAT_RUST,
obj: &wrap,
"buffer {:?} not writable",
outbuf
);
return glib_sys::GFALSE;
}
gst_panic_to_error!(&wrap, &instance.panicked(), true, {
match imp.copy_metadata(
&wrap,
gst::BufferRef::from_ptr(inbuf),
gst::BufferRef::from_mut_ptr(outbuf),
) {
Ok(_) => true,
Err(err) => {
err.log_with_object(&wrap);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn base_transform_before_transform<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
inbuf: *mut gst_sys::GstBuffer,
) where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), (), {
imp.before_transform(&wrap, gst::BufferRef::from_ptr(inbuf));
})
}
unsafe extern "C" fn base_transform_submit_input_buffer<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
is_discont: glib_sys::gboolean,
buf: *mut gst_sys::GstBuffer,
) -> gst_sys::GstFlowReturn
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
imp.submit_input_buffer(&wrap, from_glib(is_discont), from_glib_full(buf))
.into()
})
.to_glib()
}
unsafe extern "C" fn base_transform_generate_output<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
buf: *mut *mut gst_sys::GstBuffer,
) -> gst_sys::GstFlowReturn
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
*buf = ptr::null_mut();
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
match imp.generate_output(&wrap) {
Ok(GeneratedOutput::Dropped) => ::BASE_TRANSFORM_FLOW_DROPPED.into(),
Ok(GeneratedOutput::NoOutput) => gst::FlowReturn::Ok,
Ok(GeneratedOutput::Buffer(outbuf)) => {
*buf = outbuf.into_ptr();
gst::FlowReturn::Ok
}
Err(err) => err.into(),
}
})
.to_glib()
}

View file

@ -9,6 +9,7 @@
use glib::translate::mut_override;
use glib_sys;
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a>(&'a glib_sys::GMutex);
impl<'a> MutexGuard<'a> {

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-check"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Check library"
@ -14,12 +14,12 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-check-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-check-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -128,9 +128,8 @@ impl Harness {
pub fn add_sink_harness(&mut self, sink_harness: Harness) {
unsafe {
let sink_harness = mem::ManuallyDrop::new(sink_harness);
gst_check_sys::gst_harness_add_sink_harness(self.0.as_ptr(), sink_harness.0.as_ptr());
mem::forget(sink_harness);
}
}
@ -152,13 +151,12 @@ impl Harness {
pub fn add_src_harness(&mut self, src_harness: Harness, has_clock_wait: bool) {
unsafe {
let src_harness = mem::ManuallyDrop::new(src_harness);
gst_check_sys::gst_harness_add_src_harness(
self.0.as_ptr(),
src_harness.0.as_ptr(),
has_clock_wait.to_glib(),
);
mem::forget(src_harness);
}
}
@ -730,7 +728,7 @@ impl Harness {
None
} else {
Some(Ref(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(sink_harness),
PhantomData,
)),
@ -747,7 +745,7 @@ impl Harness {
None
} else {
Some(Ref(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(src_harness),
PhantomData,
)),
@ -764,7 +762,7 @@ impl Harness {
None
} else {
Some(RefMut(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(sink_harness),
PhantomData,
)),
@ -781,7 +779,7 @@ impl Harness {
None
} else {
Some(RefMut(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(src_harness),
PhantomData,
)),
@ -793,44 +791,30 @@ impl Harness {
}
#[derive(Debug)]
pub struct Ref<'a>(Option<Harness>, PhantomData<&'a Harness>);
pub struct Ref<'a>(mem::ManuallyDrop<Harness>, PhantomData<&'a Harness>);
impl<'a> ops::Deref for Ref<'a> {
type Target = Harness;
fn deref(&self) -> &Harness {
self.0.as_ref().unwrap()
}
}
impl<'a> Drop for Ref<'a> {
fn drop(&mut self) {
// We only really borrow
mem::forget(self.0.take())
&*self.0
}
}
#[derive(Debug)]
pub struct RefMut<'a>(Option<Harness>, PhantomData<&'a mut Harness>);
pub struct RefMut<'a>(mem::ManuallyDrop<Harness>, PhantomData<&'a mut Harness>);
impl<'a> ops::Deref for RefMut<'a> {
type Target = Harness;
fn deref(&self) -> &Harness {
self.0.as_ref().unwrap()
&*self.0
}
}
impl<'a> ops::DerefMut for RefMut<'a> {
fn deref_mut(&mut self) -> &mut Harness {
self.0.as_mut().unwrap()
}
}
impl<'a> Drop for RefMut<'a> {
fn drop(&mut self) {
// We only really borrow
mem::forget(self.0.take())
&mut *self.0
}
}

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-editing-services"
version = "0.15.0"
version = "0.15.7"
authors = ["Thibault Saunier <tsaunier@igalia.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Editing Services"
@ -15,16 +15,16 @@ build = "build.rs"
[dependencies]
libc = "0.2"
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gio-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-editing-services-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"]}
glib = { git = "https://github.com/gtk-rs/glib" }
gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
gstreamer-pbutils = { path = "../gstreamer-pbutils" }
glib-sys = { version = "0.9" }
gio-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-editing-services-sys = { version = "0.8", features = ["v1_8"]}
glib = { version = "0.9" }
gio = { version = "0.8" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
gstreamer-pbutils = { version = "0.15", path = "../gstreamer-pbutils" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-gl"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>",
"Víctor M. Jáquez L. <vjaquez@igalia.com>"]
categories = ["api-bindings", "multimedia"]
@ -18,15 +18,15 @@ bitflags = "1.0"
byteorder = "1"
libc = "0.2"
lazy_static = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_14"] }
gstreamer-video-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_14"] }
gstreamer-gl-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer", features = ["v1_14"] }
gstreamer-base = { path = "../gstreamer-base", features = ["v1_14"] }
gstreamer-video = { path = "../gstreamer-video", features = ["v1_14"] }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_14"] }
gstreamer-video-sys = { version = "0.8", features = ["v1_14"] }
gstreamer-gl-sys = { version = "0.8" }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer", features = ["v1_14"] }
gstreamer-base = { version = "0.15", path = "../gstreamer-base", features = ["v1_14"] }
gstreamer-video = { version = "0.15", path = "../gstreamer-video", features = ["v1_14"] }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-net"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Net library"
@ -13,13 +13,13 @@ keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"]
build = "build.rs"
[dependencies]
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-net-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gio = { git = "https://github.com/gtk-rs/gio" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-net-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gio = { version = "0.8" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-pbutils"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Base Utils library"
@ -15,12 +15,12 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-pbutils-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-pbutils-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -488,19 +488,19 @@ mod tests {
use auto::EncodingVideoProfile;
use gst;
const AUDIO_PROFILE_NAME: &'static str = "audio-profile";
const AUDIO_PROFILE_DESCRIPTION: &'static str = "audio-profile-description";
const PRESET: &'static str = "preset";
const PRESET_NAME: &'static str = "preset-name";
const AUDIO_PROFILE_NAME: &str = "audio-profile";
const AUDIO_PROFILE_DESCRIPTION: &str = "audio-profile-description";
const PRESET: &str = "preset";
const PRESET_NAME: &str = "preset-name";
const PRESENCE: u32 = 5;
const ALLOW_DYNAMIC_OUTPUT: bool = false;
const ENABLED: bool = false;
const VIDEO_PROFILE_NAME: &'static str = "video-profile";
const VIDEO_PROFILE_DESCRIPTION: &'static str = "video-profile-description";
const VIDEO_PROFILE_NAME: &str = "video-profile";
const VIDEO_PROFILE_DESCRIPTION: &str = "video-profile-description";
const CONTAINER_PROFILE_NAME: &'static str = "container-profile";
const CONTAINER_PROFILE_DESCRIPTION: &'static str = "container-profile-description";
const CONTAINER_PROFILE_NAME: &str = "container-profile";
const CONTAINER_PROFILE_DESCRIPTION: &str = "container-profile-description";
// Video profile exclusive attributes
const PASS: u32 = 8;

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-player"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Player library"
@ -15,13 +15,13 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_12"] }
gstreamer-player-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer", features = ["v1_12"] }
gstreamer-video = { path = "../gstreamer-video", features = ["v1_12"] }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_12"] }
gstreamer-player-sys = { version = "0.8" }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer", features = ["v1_12"] }
gstreamer-video = { version = "0.15", path = "../gstreamer-video", features = ["v1_12"] }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -14,7 +14,7 @@ use gst_sys;
use std::mem;
use std::ops;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PlayerConfig(gst::Structure);
impl ops::Deref for PlayerConfig {
@ -96,9 +96,9 @@ impl PlayerConfig {
}
}
pub unsafe fn into_ptr(mut self) -> *mut gst_sys::GstStructure {
let ptr = self.0.to_glib_none_mut().0;
mem::forget(self);
pub unsafe fn into_ptr(self) -> *mut gst_sys::GstStructure {
let mut s = mem::ManuallyDrop::new(self);
let ptr = s.0.to_glib_none_mut().0;
ptr
}
}

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-rtp"
version = "0.15.0"
version = "0.15.7"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Rtp library"
@ -15,12 +15,12 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
lazy_static = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtp-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies.rustdoc-stripper]
version = "0.1"
@ -34,6 +34,7 @@ v1_14 = ["gstreamer/v1_14", "gstreamer-rtp-sys/v1_14", "v1_12"]
v1_16 = ["gstreamer/v1_16", "gstreamer-rtp-sys/v1_16", "v1_14"]
embed-lgpl-docs = ["rustdoc-stripper"]
purge-lgpl-docs = ["rustdoc-stripper"]
dox = ["v1_16", "gstreamer-rtp-sys/dox", "glib/dox", "gstreamer/dox"]
[package.metadata.docs.rs]
dox = ["v1_16", "gstreamer-rtp-sys/dox", "glib/dox", "gstreamer/dox"]

196
gstreamer-rtp/README.md Normal file
View file

@ -0,0 +1,196 @@
# gstreamer-rs [![crates.io](https://img.shields.io/crates/v/gstreamer-rtp.svg)](https://crates.io/crates/gstreamer-rtp) [![pipeline status](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/badges/master/pipeline.svg)](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/commits/master)
[GStreamer](https://gstreamer.freedesktop.org/) bindings for Rust.
Documentation can be found [here](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer_rtp/).
These bindings are providing a safe API that can be used to interface with
GStreamer, e.g. for writing GStreamer-based applications and GStreamer plugins.
The bindings are mostly autogenerated with [gir](https://github.com/gtk-rs/gir/)
based on the [GObject-Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection/)
API metadata provided by the GStreamer project.
## Table of Contents
1. [Installation](#installation)
1. [Linux/BSDs](#installation-linux)
1. [macOS](#installation-macos)
1. [Windows](#installation-windows)
1. [Getting Started](#getting-started)
1. [License](#license)
1. [Contribution](#contribution)
<a name="installation"/>
## Installation
To build the GStreamer bindings or anything depending on them, you need to
have at least GStreamer 1.8 and gst-plugins-base 1.8 installed. In addition,
some of the examples/tutorials require various GStreamer plugins to be
available, which can be found in gst-plugins-base, gst-plugins-good,
gst-plugins-bad, gst-plugins-ugly and/or gst-libav.
<a name="installation-linux"/>
### Linux/BSDs
You need to install the above mentioned packages with your distributions
package manager, or build them from source.
On Debian/Ubuntu they can be installed with
```
$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
gstreamer1.0-libav libgstrtspserver-1.0-dev
```
The minimum required version of the above libraries is >= 1.8. If you
build the gstreamer-player sub-crate, or any of the examples that
depend on gstreamer-player, you must ensure that in addition to the
above packages, `libgstreamer-plugins-bad1.0-dev` is installed and
that the version is >= 1.12. See the `Cargo.toml` files for the full
details,
```
# Only if you wish to install gstreamer-player, make sure the version
# of this package is >= 1.12.
$ apt-get install libgstreamer-plugins-bad1.0-dev
```
Package names on other distributions should be similar.
Please submit a pull request with instructions for yours.
<a name="installation-macos"/>
### macOS
You can install GStreamer and the plugins via [Homebrew](https://brew.sh/) or
by installing the [binaries](https://gstreamer.freedesktop.org/data/pkg/osx/)
provided by the GStreamer project.
#### Homebrew
Homebrew only installs various plugins if explicitly enabled, so some extra
`--with-*` flags may be required.
```
$ brew install gstreamer gst-plugins-base gst-plugins-good \
gst-plugins-bad gst-plugins-ugly gst-libav gst-rtsp-server \
gst-editing-services --with-orc --with-libogg --with-opus \
--with-pango --with-theora --with-libvorbis --with-libvpx \
--enable-gtk3
```
If you wish to install the gstreamer-player sub-crate, make sure the
version of these libraries is >= 1.12. Otherwise, a version >= 1.8 is
sufficient.
#### GStreamer Binaries
You need to download the *two* `.pkg` files from the GStreamer website and
install them, e.g. `gstreamer-1.0-1.12.3-x86_64.pkg` and
`gstreamer-1.0-devel-1.12.3-x86_64.pkg`.
After installation, you also need to install `pkg-config` (e.g. via Homebrew)
and set the `PKG_CONFIG_PATH` environment variable
```
$ export PKG_CONFIG_PATH="/Library/Frameworks/GStreamer.framework/Versions/Current/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
```
<a name="installation-windows"/>
### Windows
You can install GStreamer and the plugins via [MSYS2](http://www.msys2.org/)
with `pacman` or by installing the
[binaries](https://gstreamer.freedesktop.org/data/pkg/windows/) provided by
the GStreamer project.
#### MSYS2 / pacman
```
$ pacman -S pkg-config mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base \
mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad \
mingw-w64-x86_64-gst-plugins-ugly mingw-w64-x86_64-gst-libav \
mingw-w64-x86_64-gst-rtsp-server
```
If you wish to install the gstreamer-player sub-crate, make sure the
version of these libraries is >= 1.12. Otherwise, a version >= 1.8 is
sufficient.
#### GStreamer Binaries
You need to download the *two* `.msi` files for your platform from the
GStreamer website and install them, e.g. `gstreamer-1.0-x86_64-1.12.3.msi` and
`gstreamer-1.0-devel-x86_64-1.12.3.msi`.
After installation, you also need to install `pkg-config` (e.g. via MSYS2 or
from [here](https://sourceforge.net/projects/pkgconfiglite/))
and set the `PKG_CONFIG_PATH` environment variable
```
$ export PKG_CONFIG_PATH="c:\\gstreamer\\1.0\\x86_64\\lib\\pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
```
<a name="getting-started"/>
## Getting Started
The API reference can be found
[here](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/), however it is
only the Rust API reference and does not explain any of the concepts.
For getting started with GStreamer development, the best would be to follow
the [documentation](https://gstreamer.freedesktop.org/documentation/) on the
GStreamer website, especially the [Application Development
Manual](https://gstreamer.freedesktop.org/documentation/application-development/).
While being C-centric, it explains all the fundamental concepts of GStreamer
and the code examples should be relatively easily translatable to Rust. The
API is basically the same, function/struct names are the same and everything
is only more convenient (hopefully) and safer.
In addition there are
[tutorials](https://gstreamer.freedesktop.org/documentation/tutorials/) on the
GStreamer website. Many of them were ported to Rust already and the code can
be found in the
[tutorials](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/master/tutorials)
directory.
Some further examples for various aspects of GStreamer and how to use it from
Rust can be found in the
[examples](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/master/examples)
directory.
Various GStreamer plugins written in Rust can be found in the
[gst-plugins-rs](https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs)
repository.
<a name="license"/>
## LICENSE
gstreamer-rs and all crates contained in here are licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
GStreamer itself is licensed under the Lesser General Public License version
2.1 or (at your option) any later version:
https://www.gnu.org/licenses/lgpl-2.1.html
<a name="contribution"/>
## Contribution
Any kinds of contributions are welcome as a pull request.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in gstreamer-rs by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View file

@ -1,6 +1,9 @@
use glib::translate::{from_glib, FromGlibPtrFull};
use glib::translate::{from_glib, from_glib_full, FromGlibPtrFull, ToGlib};
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::slice;
use gst::MiniObject;
use gst_rtp_sys;
@ -8,12 +11,25 @@ use gst_rtp_sys;
pub enum Readable {}
pub enum Writable {}
#[repr(C)]
pub struct RTPBuffer<'a, T>(gst_rtp_sys::GstRTPBuffer, &'a gst::Buffer, PhantomData<T>);
pub struct RTPBuffer<'a, T> {
rtp_buffer: gst_rtp_sys::GstRTPBuffer,
buffer: &'a gst::Buffer,
phantom: PhantomData<T>,
}
unsafe impl<'a, T> Send for RTPBuffer<'a, T> {}
unsafe impl<'a, T> Sync for RTPBuffer<'a, T> {}
impl<'a, T> fmt::Debug for RTPBuffer<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RTPBuffer")
.field("rtp_buffer", &self.rtp_buffer)
.field("buffer", &self.buffer)
.field("phantom", &self.phantom)
.finish()
}
}
impl<'a> RTPBuffer<'a, Readable> {
pub fn from_buffer_readable(
buffer: &gst::Buffer,
@ -27,7 +43,11 @@ impl<'a> RTPBuffer<'a, Readable> {
));
if res {
Ok(RTPBuffer(rtp_buffer.assume_init(), buffer, PhantomData))
Ok(RTPBuffer {
rtp_buffer: rtp_buffer.assume_init(),
buffer,
phantom: PhantomData,
})
} else {
Err(glib_bool_error!("Failed to map RTP buffer readable"))
}
@ -48,7 +68,11 @@ impl<'a> RTPBuffer<'a, Writable> {
));
if res {
Ok(RTPBuffer(rtp_buffer.assume_init(), buffer, PhantomData))
Ok(RTPBuffer {
rtp_buffer: rtp_buffer.assume_init(),
buffer,
phantom: PhantomData,
})
} else {
Err(glib_bool_error!("Failed to map RTP buffer writable"))
}
@ -57,41 +81,243 @@ impl<'a> RTPBuffer<'a, Writable> {
pub fn set_seq(&mut self, seq: u16) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_seq(&mut self.0, seq);
gst_rtp_sys::gst_rtp_buffer_set_seq(&mut self.rtp_buffer, seq);
}
}
pub fn set_payload_type(&mut self, pt: u8) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_payload_type(&mut self.0, pt);
gst_rtp_sys::gst_rtp_buffer_set_payload_type(&mut self.rtp_buffer, pt);
}
}
pub fn set_ssrc(&mut self, ssrc: u32) {
unsafe { gst_rtp_sys::gst_rtp_buffer_set_ssrc(&mut self.rtp_buffer, ssrc) }
}
pub fn set_csrc(&mut self, idx: u8, ssrc: u32) {
unsafe { gst_rtp_sys::gst_rtp_buffer_set_csrc(&mut self.rtp_buffer, idx, ssrc) }
}
pub fn set_timestamp(&mut self, rtptime: u32) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_timestamp(&mut self.0, rtptime);
gst_rtp_sys::gst_rtp_buffer_set_timestamp(&mut self.rtp_buffer, rtptime);
}
}
pub fn set_extension(&mut self, extension: bool) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_extension(&mut self.rtp_buffer, extension.to_glib())
}
}
pub fn add_extension_onebyte_header(
&mut self,
id: u8,
data: &[u8],
) -> Result<(), glib::BoolError> {
assert!(
id >= 1 && id <= 14,
"id should be between 1 and 14 (inclusive)"
);
assert!(
!data.is_empty() && data.len() <= 16,
"data size should be between 1 and 16 (inclusive"
);
unsafe {
let result: bool = from_glib(gst_rtp_sys::gst_rtp_buffer_add_extension_onebyte_header(
&mut self.rtp_buffer,
id,
data.as_ptr() as glib_sys::gconstpointer,
data.len() as u32,
));
if result {
Ok(())
} else {
Err(glib_bool_error!("Failed to add onebyte header extension"))
}
}
}
pub fn add_extension_twobytes_header(
&mut self,
appbits: u8,
id: u8,
data: &[u8],
) -> Result<(), glib::BoolError> {
assert_eq!(
appbits & 0xF0,
0,
"appbits must use only 4 bits (max value is 15)"
);
assert!(data.len() < 256, "data size should be smaller than 256");
unsafe {
let result: bool =
from_glib(gst_rtp_sys::gst_rtp_buffer_add_extension_twobytes_header(
&mut self.rtp_buffer,
appbits,
id,
data.as_ptr() as glib_sys::gconstpointer,
data.len() as u32,
));
if result {
Ok(())
} else {
Err(glib_bool_error!("Failed to add twobytes header extension"))
}
}
}
}
impl<'a, T> RTPBuffer<'a, T> {
pub fn get_seq(&mut self) -> u16 {
unsafe { gst_rtp_sys::gst_rtp_buffer_get_seq(&mut self.0) }
pub fn get_seq(&self) -> u16 {
unsafe {
gst_rtp_sys::gst_rtp_buffer_get_seq(glib::translate::mut_override(&self.rtp_buffer))
}
}
pub fn get_payload_type(&mut self) -> u8 {
unsafe { gst_rtp_sys::gst_rtp_buffer_get_payload_type(&mut self.0) }
pub fn get_payload_type(&self) -> u8 {
unsafe {
gst_rtp_sys::gst_rtp_buffer_get_payload_type(glib::translate::mut_override(
&self.rtp_buffer,
))
}
}
pub fn get_timestamp(&mut self) -> u32 {
unsafe { gst_rtp_sys::gst_rtp_buffer_get_timestamp(&mut self.0) }
pub fn get_ssrc(&self) -> u32 {
unsafe {
gst_rtp_sys::gst_rtp_buffer_get_ssrc(glib::translate::mut_override(&self.rtp_buffer))
}
}
pub fn get_timestamp(&self) -> u32 {
unsafe {
gst_rtp_sys::gst_rtp_buffer_get_timestamp(glib::translate::mut_override(
&self.rtp_buffer,
))
}
}
pub fn get_csrc(&self, idx: u8) -> Option<u32> {
if idx < self.get_csrc_count() {
unsafe {
Some(gst_rtp_sys::gst_rtp_buffer_get_csrc(
glib::translate::mut_override(&self.rtp_buffer),
idx,
))
}
} else {
None
}
}
pub fn get_csrc_count(&self) -> u8 {
unsafe {
gst_rtp_sys::gst_rtp_buffer_get_csrc_count(glib::translate::mut_override(
&self.rtp_buffer,
))
}
}
pub fn get_payload_size(&self) -> u32 {
unsafe {
gst_rtp_sys::gst_rtp_buffer_get_payload_len(glib::translate::mut_override(
&self.rtp_buffer,
)) as u32
}
}
pub fn get_payload(&self) -> Result<&[u8], glib::error::BoolError> {
let size = self.get_payload_size();
if size == 0 {
return Ok(&[]);
}
unsafe {
let pointer = gst_rtp_sys::gst_rtp_buffer_get_payload(glib::translate::mut_override(
&self.rtp_buffer,
));
if pointer.is_null() {
Err(glib_bool_error!("Failed to get payload data"))
} else {
Ok(slice::from_raw_parts(pointer as *const u8, size as usize))
}
}
}
pub fn get_extension(&self) -> bool {
unsafe {
from_glib(gst_rtp_sys::gst_rtp_buffer_get_extension(
glib::translate::mut_override(&self.rtp_buffer),
))
}
}
pub fn get_extension_bytes(&self) -> Option<(u16, glib::Bytes)> {
unsafe {
let mut bits: u16 = 0;
match from_glib_full(gst_rtp_sys::gst_rtp_buffer_get_extension_bytes(
glib::translate::mut_override(&self.rtp_buffer),
&mut bits,
)) {
Some(bytes) => Some((bits, bytes)),
None => None,
}
}
}
pub fn get_extension_onebyte_header(&self, id: u8, nth: u32) -> Option<&[u8]> {
unsafe {
let mut data = ptr::null_mut();
// FIXME: Workaround for gstreamer-rtp-sys having the wrong type for this parameter
let data_ptr = &mut data as *mut *mut u8 as *mut u8;
let mut size: u32 = 0;
let result: bool = from_glib(gst_rtp_sys::gst_rtp_buffer_get_extension_onebyte_header(
glib::translate::mut_override(&self.rtp_buffer),
id,
nth,
data_ptr,
&mut size,
));
if result {
Some(slice::from_raw_parts(data as *const u8, size as usize))
} else {
None
}
}
}
pub fn get_extension_twobytes_header(&self, id: u8, nth: u32) -> Option<(u8, &[u8])> {
unsafe {
let mut data = ptr::null_mut();
// FIXME: Workaround for gstreamer-rtp-sys having the wrong type for this parameter
let data_ptr = &mut data as *mut *mut u8 as *mut u8;
let mut size: u32 = 0;
let mut appbits = 0;
let result: bool =
from_glib(gst_rtp_sys::gst_rtp_buffer_get_extension_twobytes_header(
glib::translate::mut_override(&self.rtp_buffer),
&mut appbits,
id,
nth,
data_ptr,
&mut size,
));
if result {
Some((
appbits,
slice::from_raw_parts(data as *const u8, size as usize),
))
} else {
None
}
}
}
}
impl<'a, T> Drop for RTPBuffer<'a, T> {
fn drop(&mut self) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_unmap(&mut self.0);
gst_rtp_sys::gst_rtp_buffer_unmap(&mut self.rtp_buffer);
}
}
}
@ -133,7 +359,9 @@ mod tests {
fn test_map() {
gst::init().unwrap();
let mut buffer = gst::Buffer::new_rtp_with_sizes(16, 4, 0).unwrap();
let csrc_count = 2;
let payload_size = 16;
let mut buffer = gst::Buffer::new_rtp_with_sizes(payload_size, 4, csrc_count).unwrap();
let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buffer).unwrap();
rtp_buffer.set_seq(42);
@ -144,5 +372,122 @@ mod tests {
rtp_buffer.set_timestamp(44);
assert_eq!(rtp_buffer.get_timestamp(), 44);
rtp_buffer.set_ssrc(45);
assert_eq!(rtp_buffer.get_ssrc(), 45);
assert_eq!(rtp_buffer.get_payload_size(), payload_size);
let payload = rtp_buffer.get_payload();
assert!(payload.is_ok());
let payload = payload.unwrap();
assert_eq!(payload.len(), payload_size as usize);
assert_eq!(rtp_buffer.get_csrc_count(), csrc_count);
rtp_buffer.set_csrc(0, 12);
rtp_buffer.set_csrc(1, 15);
assert_eq!(rtp_buffer.get_csrc(0).unwrap(), 12);
assert_eq!(rtp_buffer.get_csrc(1).unwrap(), 15);
assert!(rtp_buffer.get_csrc(2).is_none());
rtp_buffer.set_extension(true);
assert_eq!(rtp_buffer.get_extension(), true);
assert_eq!(rtp_buffer.get_extension_bytes(), None);
}
#[test]
fn test_empty_payload() {
gst::init().unwrap();
let csrc_count = 0;
let payload_size = 0;
let buffer = gst::Buffer::new_rtp_with_sizes(payload_size, 4, csrc_count).unwrap();
let rtp_buffer = RTPBuffer::from_buffer_readable(&buffer).unwrap();
assert_eq!(rtp_buffer.get_payload_size(), payload_size);
let payload = rtp_buffer.get_payload();
assert!(payload.is_ok());
assert_eq!(payload.unwrap().len(), payload_size as usize);
}
#[test]
fn test_extension_header_onebyte() {
gst::init().unwrap();
let mut buffer = gst::Buffer::new_rtp_with_sizes(16, 4, 0).unwrap();
let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buffer).unwrap();
assert_eq!(rtp_buffer.get_extension_bytes(), None);
let extension_data: [u8; 4] = [100, 101, 102, 103];
let result = rtp_buffer.add_extension_onebyte_header(1, &extension_data);
assert!(result.is_ok());
let bytes_option = rtp_buffer.get_extension_bytes();
assert!(bytes_option.is_some());
let (bits, bytes) = bytes_option.unwrap();
// 0xBEDE is the onebyte extension header marker: https://tools.ietf.org/html/rfc5285 (4.2)
assert_eq!(bits, 0xbede);
/*
* bytes is:
* * id (4 bits)
* * size-1 (4 bits)
* * data (with padded length to multiples of 4)
*/
assert_eq!(bytes[0] >> 4, 1);
assert_eq!(bytes[0] & 0xF, 3);
for i in 0..extension_data.len() {
assert_eq!(bytes[i + 1], extension_data[i]);
}
let result = rtp_buffer.get_extension_onebyte_header(2, 0);
assert!(result.is_none());
let result = rtp_buffer.get_extension_onebyte_header(1, 0);
assert!(result.is_some());
assert_eq!(result.unwrap(), &extension_data);
}
#[test]
fn test_extension_header_twobytes() {
gst::init().unwrap();
let mut buffer = gst::Buffer::new_rtp_with_sizes(16, 4, 0).unwrap();
let mut rtp_buffer = RTPBuffer::from_buffer_writable(&mut buffer).unwrap();
assert_eq!(rtp_buffer.get_extension_bytes(), None);
let extension_data: [u8; 4] = [100, 101, 102, 103];
let appbits = 5;
let id = 1;
let result = rtp_buffer.add_extension_twobytes_header(appbits, id, &extension_data);
assert!(result.is_ok());
let bytes_option = rtp_buffer.get_extension_bytes();
assert!(bytes_option.is_some());
let (bits, bytes) = bytes_option.unwrap();
// 0x100 + appbits is the twobyte extension header marker:
// https://tools.ietf.org/html/rfc5285 (4.3)
assert_eq!(bits, 0x1000 | appbits as u16);
/*
* bytes is:
* * id (1 byte)
* * size-2 (1 byte)
* * data (with padded length to multiples of 4)
*/
assert_eq!(bytes[0], id);
assert_eq!(bytes[1], extension_data.len() as u8);
for i in 0..extension_data.len() {
assert_eq!(bytes[i + 2], extension_data[i]);
}
let result = rtp_buffer.get_extension_twobytes_header(2, 0);
assert!(result.is_none());
let result = rtp_buffer.get_extension_twobytes_header(id, 0);
assert!(result.is_some());
let (extracted_appbits, data) = result.unwrap();
assert_eq!(appbits, extracted_appbits);
assert_eq!(data, &extension_data);
}
}

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-rtsp-server"
version = "0.15.0"
version = "0.15.7"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer RTSP Server library"
@ -16,18 +16,20 @@ build = "build.rs"
bitflags = "1.0"
libc = "0.2"
lazy_static = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gio-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtsp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtsp-server-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-net-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { path = "../gstreamer" }
gstreamer-rtsp = { path = "../gstreamer-rtsp" }
gstreamer-net = { path = "../gstreamer-net" }
glib-sys = { version = "0.9" }
gio-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-sdp-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtsp-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtsp-server-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-net-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gio = { version = "0.8" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp" }
gstreamer-rtsp = { version = "0.15", path = "../gstreamer-rtsp" }
gstreamer-net = { version = "0.15", path = "../gstreamer-net" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -24,6 +24,7 @@ use RTSPMediaStatus;
use RTSPPublishClockMode;
use RTSPStream;
use RTSPSuspendMode;
use RTSPThread;
use RTSPTransportMode;
glib_wrapper! {
@ -123,7 +124,7 @@ pub trait RTSPMediaExt: 'static {
fn n_streams(&self) -> u32;
//fn prepare(&self, thread: /*Ignored*/Option<&mut RTSPThread>) -> bool;
fn prepare(&self, thread: Option<&RTSPThread>) -> Result<(), glib::error::BoolError>;
//fn seek(&self, range: /*Ignored*/&mut gst_rtsp::RTSPTimeRange) -> bool;
@ -182,8 +183,6 @@ pub trait RTSPMediaExt: 'static {
fn suspend(&self) -> Result<(), glib::error::BoolError>;
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P);
fn unprepare(&self) -> Result<(), glib::error::BoolError>;
fn unsuspend(&self) -> Result<(), glib::error::BoolError>;
@ -542,9 +541,17 @@ impl<O: IsA<RTSPMedia>> RTSPMediaExt for O {
unsafe { gst_rtsp_server_sys::gst_rtsp_media_n_streams(self.as_ref().to_glib_none().0) }
}
//fn prepare(&self, thread: /*Ignored*/Option<&mut RTSPThread>) -> bool {
// unsafe { TODO: call gst_rtsp_server_sys:gst_rtsp_media_prepare() }
//}
fn prepare(&self, thread: Option<&RTSPThread>) -> Result<(), glib::error::BoolError> {
unsafe {
glib_result_from_gboolean!(
gst_rtsp_server_sys::gst_rtsp_media_prepare(
self.as_ref().to_glib_none().0,
thread.to_glib_full()
),
"Failed to prepare media"
)
}
}
//fn seek(&self, range: /*Ignored*/&mut gst_rtsp::RTSPTimeRange) -> bool {
// unsafe { TODO: call gst_rtsp_server_sys:gst_rtsp_media_seek() }
@ -755,15 +762,6 @@ impl<O: IsA<RTSPMedia>> RTSPMediaExt for O {
}
}
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P) {
unsafe {
gst_rtsp_server_sys::gst_rtsp_media_take_pipeline(
self.as_ref().to_glib_none().0,
pipeline.as_ref().to_glib_full(),
);
}
}
fn unprepare(&self) -> Result<(), glib::error::BoolError> {
unsafe {
glib_result_from_gboolean!(

View file

@ -11,6 +11,9 @@ use glib_sys;
use gst_rtsp_server_sys;
use std::boxed::Box as Box_;
use std::mem::transmute;
use RTSPContext;
use RTSPThread;
use RTSPThreadType;
glib_wrapper! {
pub struct RTSPThreadPool(Object<gst_rtsp_server_sys::GstRTSPThreadPool, gst_rtsp_server_sys::GstRTSPThreadPoolClass, RTSPThreadPoolClass>);
@ -48,7 +51,7 @@ pub const NONE_RTSP_THREAD_POOL: Option<&RTSPThreadPool> = None;
pub trait RTSPThreadPoolExt: 'static {
fn get_max_threads(&self) -> i32;
//fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> /*Ignored*/Option<RTSPThread>;
fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> Option<RTSPThread>;
fn set_max_threads(&self, max_threads: i32);
@ -67,9 +70,15 @@ impl<O: IsA<RTSPThreadPool>> RTSPThreadPoolExt for O {
}
}
//fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> /*Ignored*/Option<RTSPThread> {
// unsafe { TODO: call gst_rtsp_server_sys:gst_rtsp_thread_pool_get_thread() }
//}
fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> Option<RTSPThread> {
unsafe {
from_glib_full(gst_rtsp_server_sys::gst_rtsp_thread_pool_get_thread(
self.as_ref().to_glib_none().0,
type_.to_glib(),
ctx.to_glib_none().0,
))
}
}
fn set_max_threads(&self, max_threads: i32) {
unsafe {

View file

@ -26,6 +26,8 @@ extern crate gstreamer_net_sys as gst_net_sys;
extern crate gstreamer_rtsp as gst_rtsp;
extern crate gstreamer_rtsp_server_sys as gst_rtsp_server_sys;
extern crate gstreamer_rtsp_sys as gst_rtsp_sys;
extern crate gstreamer_sdp as gst_sdp;
extern crate gstreamer_sdp_sys as gst_sdp_sys;
extern crate gstreamer_sys as gst_sys;
macro_rules! assert_initialized_main_thread {
@ -52,21 +54,27 @@ mod rtsp_address_pool;
mod rtsp_auth;
mod rtsp_client;
mod rtsp_context;
mod rtsp_media;
mod rtsp_media_factory;
mod rtsp_server;
mod rtsp_session_pool;
mod rtsp_stream;
mod rtsp_stream_transport;
mod rtsp_thread;
mod rtsp_token;
pub mod subclass;
pub use rtsp_address_pool::RTSPAddressPoolExtManual;
pub use rtsp_auth::RTSPAuthExtManual;
pub use rtsp_client::RTSPClientExtManual;
pub use rtsp_media::RTSPMediaExtManual;
pub use rtsp_media_factory::RTSPMediaFactoryExtManual;
pub use rtsp_server::RTSPServerExtManual;
pub use rtsp_session_pool::RTSPSessionPoolExtManual;
pub use rtsp_stream::RTSPStreamExtManual;
pub use rtsp_stream_transport::RTSPStreamTransportExtManual;
pub use rtsp_thread::*;
pub use rtsp_context::*;
pub use rtsp_token::*;
@ -140,6 +148,7 @@ pub mod prelude {
pub use rtsp_address_pool::RTSPAddressPoolExtManual;
pub use rtsp_auth::RTSPAuthExtManual;
pub use rtsp_client::RTSPClientExtManual;
pub use rtsp_media::RTSPMediaExtManual;
pub use rtsp_media_factory::RTSPMediaFactoryExtManual;
pub use rtsp_server::RTSPServerExtManual;
pub use rtsp_session_pool::RTSPSessionPoolExtManual;

View file

@ -37,3 +37,18 @@ impl glib::translate::FromGlibPtrBorrow<*mut gst_rtsp_server_sys::GstRTSPContext
RTSPContext(ptr::NonNull::new_unchecked(ptr))
}
}
#[doc(hidden)]
impl<'a> glib::translate::ToGlibPtr<'a, *mut gst_rtsp_server_sys::GstRTSPContext> for RTSPContext {
type Storage = &'a RTSPContext;
fn to_glib_none(
&'a self,
) -> glib::translate::Stash<'a, *mut gst_rtsp_server_sys::GstRTSPContext, Self> {
glib::translate::Stash(self.0.as_ptr(), self)
}
fn to_glib_full(&self) -> *mut gst_rtsp_server_sys::GstRTSPContext {
unimplemented!()
}
}

View file

@ -0,0 +1,27 @@
use glib::object::IsA;
use glib::translate::*;
use gst;
use gst_rtsp_server_sys;
use RTSPMedia;
pub trait RTSPMediaExtManual: 'static {
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P);
}
impl<O: IsA<RTSPMedia>> RTSPMediaExtManual for O {
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P) {
unsafe {
let pipeline = pipeline.as_ref().to_glib_full();
// See https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/merge_requests/109
gobject_sys::g_object_force_floating(pipeline as *mut _);
gst_rtsp_server_sys::gst_rtsp_media_take_pipeline(
self.as_ref().to_glib_none().0,
pipeline,
);
if gobject_sys::g_object_is_floating(pipeline as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(pipeline as *mut _);
}
}
}
}

View file

@ -0,0 +1,47 @@
use glib;
use glib::translate::*;
use gst_rtsp_server_sys;
use gst::prelude::*;
gst_define_mini_object_wrapper!(
RTSPThread,
RTSPThreadRef,
gst_rtsp_server_sys::GstRTSPThread,
[],
|| gst_rtsp_server_sys::gst_rtsp_thread_get_type()
);
impl RTSPThread {
pub fn new(type_: ::RTSPThreadType) -> Option<Self> {
unsafe { from_glib_full(gst_rtsp_server_sys::gst_rtsp_thread_new(type_.to_glib())) }
}
}
impl RTSPThreadRef {
pub fn reuse(&self) -> bool {
unsafe {
from_glib(gst_rtsp_server_sys::gst_rtsp_thread_reuse(
self.as_mut_ptr(),
))
}
}
pub fn stop(&self) {
unsafe {
gst_rtsp_server_sys::gst_rtsp_thread_stop(self.as_mut_ptr());
}
}
pub fn type_(&self) -> ::RTSPThreadType {
unsafe { from_glib((*self.as_ptr()).type_) }
}
pub fn context(&self) -> glib::MainContext {
unsafe { from_glib_none((*self.as_ptr()).context) }
}
pub fn loop_(&self) -> glib::MainLoop {
unsafe { from_glib_none((*self.as_ptr()).loop_) }
}
}

View file

@ -0,0 +1,20 @@
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(clippy::cast_ptr_alignment)]
pub mod rtsp_client;
pub mod rtsp_media;
pub mod rtsp_media_factory;
pub mod rtsp_server;
pub mod prelude {
pub use super::rtsp_client::{RTSPClientImpl, RTSPClientImplExt};
pub use super::rtsp_media::{RTSPMediaImpl, RTSPMediaImplExt};
pub use super::rtsp_media_factory::{RTSPMediaFactoryImpl, RTSPMediaFactoryImplExt};
pub use super::rtsp_server::{RTSPServerImpl, RTSPServerImplExt};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,780 @@
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst_rtsp_server_sys;
use glib::translate::*;
use glib::subclass::prelude::*;
use gst::prelude::*;
use std::ptr;
use RTSPMedia;
use RTSPMediaClass;
use RTSPThread;
#[derive(Debug)]
pub struct SDPInfo(ptr::NonNull<gst_rtsp_server_sys::GstSDPInfo>);
impl SDPInfo {
pub fn is_ipv6(&self) -> bool {
unsafe { from_glib(self.0.as_ref().is_ipv6) }
}
pub fn server_ip(&self) -> &str {
unsafe {
use std::ffi::CStr;
CStr::from_ptr(self.0.as_ref().server_ip).to_str().unwrap()
}
}
}
pub trait RTSPMediaImpl: RTSPMediaImplExt + ObjectImpl + Send + Sync + 'static {
fn handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool {
self.parent_handle_message(media, message)
}
fn prepare(&self, media: &RTSPMedia, thread: &RTSPThread) -> Result<(), gst::LoggableError> {
self.parent_prepare(media, thread)
}
fn unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
self.parent_unprepare(media)
}
fn suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
self.parent_suspend(media)
}
fn unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
self.parent_unsuspend(media)
}
// TODO missing: convert_range
fn query_position(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
self.parent_query_position(media)
}
fn query_stop(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
self.parent_query_stop(media)
}
fn create_rtpbin(&self, media: &RTSPMedia) -> Option<gst::Element> {
self.parent_create_rtpbin(media)
}
fn setup_rtpbin(
&self,
media: &RTSPMedia,
rtpbin: &gst::Element,
) -> Result<(), gst::LoggableError> {
self.parent_setup_rtpbin(media, rtpbin)
}
fn setup_sdp(
&self,
media: &RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &SDPInfo,
) -> Result<(), gst::LoggableError> {
self.parent_setup_sdp(media, sdp, info)
}
fn new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
self.parent_new_stream(media, stream);
}
fn removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
self.parent_removed_stream(media, stream);
}
fn prepared(&self, media: &RTSPMedia) {
self.parent_prepared(media);
}
fn unprepared(&self, media: &RTSPMedia) {
self.parent_unprepared(media);
}
fn target_state(&self, media: &RTSPMedia, state: gst::State) {
self.parent_target_state(media, state);
}
fn new_state(&self, media: &RTSPMedia, state: gst::State) {
self.parent_new_state(media, state);
}
fn handle_sdp(
&self,
media: &RTSPMedia,
sdp: &gst_sdp::SDPMessageRef,
) -> Result<(), gst::LoggableError> {
self.parent_handle_sdp(media, sdp)
}
}
pub trait RTSPMediaImplExt {
fn parent_handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool;
fn parent_prepare(
&self,
media: &RTSPMedia,
thread: &RTSPThread,
) -> Result<(), gst::LoggableError>;
fn parent_unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>;
fn parent_suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>;
fn parent_unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>;
// TODO missing: convert_range
fn parent_query_position(&self, media: &RTSPMedia) -> Option<gst::ClockTime>;
fn parent_query_stop(&self, media: &RTSPMedia) -> Option<gst::ClockTime>;
fn parent_create_rtpbin(&self, media: &RTSPMedia) -> Option<gst::Element>;
fn parent_setup_rtpbin(
&self,
media: &RTSPMedia,
rtpbin: &gst::Element,
) -> Result<(), gst::LoggableError>;
fn parent_setup_sdp(
&self,
media: &RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &SDPInfo,
) -> Result<(), gst::LoggableError>;
fn parent_new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream);
fn parent_removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream);
fn parent_prepared(&self, media: &RTSPMedia);
fn parent_unprepared(&self, media: &RTSPMedia);
fn parent_target_state(&self, media: &RTSPMedia, state: gst::State);
fn parent_new_state(&self, media: &RTSPMedia, state: gst::State);
fn parent_handle_sdp(
&self,
media: &RTSPMedia,
sdp: &gst_sdp::SDPMessageRef,
) -> Result<(), gst::LoggableError>;
}
impl<T: RTSPMediaImpl + ObjectImpl> RTSPMediaImplExt for T {
fn parent_handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).handle_message {
from_glib(f(media.to_glib_none().0, message.as_ptr() as *mut _))
} else {
false
}
}
}
fn parent_prepare(
&self,
media: &RTSPMedia,
thread: &RTSPThread,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).prepare {
gst_result_from_gboolean!(
f(media.to_glib_none().0, thread.to_glib_none().0),
gst::CAT_RUST,
"Parent function `prepare` failed"
)
} else {
Ok(())
}
}
}
fn parent_unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).unprepare {
gst_result_from_gboolean!(
f(media.to_glib_none().0),
gst::CAT_RUST,
"Parent function `unprepare` failed"
)
} else {
Ok(())
}
}
}
fn parent_suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).suspend {
gst_result_from_gboolean!(
f(media.to_glib_none().0),
gst::CAT_RUST,
"Parent function `suspend` failed"
)
} else {
Ok(())
}
}
}
fn parent_unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).unsuspend {
gst_result_from_gboolean!(
f(media.to_glib_none().0),
gst::CAT_RUST,
"Parent function `unsuspend` failed"
)
} else {
Ok(())
}
}
}
// TODO missing: convert_range
fn parent_query_position(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
unsafe {
use std::mem;
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).query_position {
let mut position = mem::MaybeUninit::uninit();
if f(media.to_glib_none().0, position.as_mut_ptr()) == glib_sys::GFALSE {
None
} else {
Some(from_glib(position.assume_init() as u64))
}
} else {
None
}
}
}
fn parent_query_stop(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
unsafe {
use std::mem;
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).query_stop {
let mut stop = mem::MaybeUninit::uninit();
if f(media.to_glib_none().0, stop.as_mut_ptr()) == glib_sys::GFALSE {
None
} else {
Some(from_glib(stop.assume_init() as u64))
}
} else {
None
}
}
}
fn parent_create_rtpbin(&self, media: &RTSPMedia) -> Option<gst::Element> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
let f = (*parent_class)
.create_rtpbin
.expect("No `create_rtpbin` virtual method implementation in parent class");
from_glib_none(f(media.to_glib_none().0))
}
}
fn parent_setup_rtpbin(
&self,
media: &RTSPMedia,
rtpbin: &gst::Element,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).setup_rtpbin {
let ptr = rtpbin.to_glib_none().0;
// The C code assumes to pass a floating reference around so let's make sure we do
gobject_sys::g_object_force_floating(ptr as *mut _);
let res = gst_result_from_gboolean!(
f(media.to_glib_none().0, ptr),
gst::CAT_RUST,
"Parent function `setup_sdp` failed"
);
// If the code didn't accidentally sink it then we have to do that
// here now so that we don't have any floating reference on our side
// anymore
if gobject_sys::g_object_is_floating(ptr as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(ptr as *mut _);
}
res
} else {
Ok(())
}
}
}
fn parent_setup_sdp(
&self,
media: &RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &SDPInfo,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
let f = (*parent_class)
.setup_sdp
.expect("No `setup_sdp` virtual method implementation in parent class");
gst_result_from_gboolean!(
f(
media.to_glib_none().0,
sdp as *mut _ as *mut gst_sdp_sys::GstSDPMessage,
info.0.as_ptr()
),
gst::CAT_RUST,
"Parent function `setup_sdp` failed"
)
}
}
fn parent_new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).new_stream {
f(media.to_glib_none().0, stream.to_glib_none().0);
}
}
}
fn parent_removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).removed_stream {
f(media.to_glib_none().0, stream.to_glib_none().0);
}
}
}
fn parent_prepared(&self, media: &RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).prepared {
f(media.to_glib_none().0);
}
}
}
fn parent_unprepared(&self, media: &RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).unprepared {
f(media.to_glib_none().0);
}
}
}
fn parent_target_state(&self, media: &RTSPMedia, state: gst::State) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).target_state {
f(media.to_glib_none().0, state.to_glib());
}
}
}
fn parent_new_state(&self, media: &RTSPMedia, state: gst::State) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).new_state {
f(media.to_glib_none().0, state.to_glib());
}
}
}
fn parent_handle_sdp(
&self,
media: &RTSPMedia,
sdp: &gst_sdp::SDPMessageRef,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
let f = (*parent_class)
.handle_sdp
.expect("No `handle_sdp` virtual method implementation in parent class");
gst_result_from_gboolean!(
f(
media.to_glib_none().0,
sdp as *const _ as *mut gst_sdp_sys::GstSDPMessage
),
gst::CAT_RUST,
"Parent function `handle_sdp` failed"
)
}
}
}
unsafe impl<T: ObjectSubclass + RTSPMediaImpl> IsSubclassable<T> for RTSPMediaClass {
fn override_vfuncs(&mut self) {
<glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass = &mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPMediaClass);
klass.handle_message = Some(media_handle_message::<T>);
klass.prepare = Some(media_prepare::<T>);
klass.unprepare = Some(media_unprepare::<T>);
klass.suspend = Some(media_suspend::<T>);
klass.unsuspend = Some(media_unsuspend::<T>);
klass.query_position = Some(media_query_position::<T>);
klass.query_stop = Some(media_query_stop::<T>);
klass.create_rtpbin = Some(media_create_rtpbin::<T>);
klass.setup_rtpbin = Some(media_setup_rtpbin::<T>);
klass.setup_sdp = Some(media_setup_sdp::<T>);
klass.new_stream = Some(media_new_stream::<T>);
klass.removed_stream = Some(media_removed_stream::<T>);
klass.prepared = Some(media_prepared::<T>);
klass.unprepared = Some(media_unprepared::<T>);
klass.target_state = Some(media_target_state::<T>);
klass.new_state = Some(media_new_state::<T>);
klass.handle_sdp = Some(media_handle_sdp::<T>);
}
}
}
unsafe extern "C" fn media_handle_message<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
message: *mut gst_sys::GstMessage,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.handle_message(&wrap, gst::MessageRef::from_ptr(message))
.to_glib()
}
unsafe extern "C" fn media_prepare<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
thread: *mut gst_rtsp_server_sys::GstRTSPThread,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.prepare(&wrap, &from_glib_borrow(thread)) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_unprepare<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.unprepare(&wrap) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_suspend<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.suspend(&wrap) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_unsuspend<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.unsuspend(&wrap) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_query_position<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
position: *mut i64,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.query_position(&wrap) {
Some(pos) => {
*position = pos.to_glib() as i64;
glib_sys::GTRUE
}
None => glib_sys::GFALSE,
}
}
unsafe extern "C" fn media_query_stop<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
stop: *mut i64,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.query_stop(&wrap) {
Some(s) => {
*stop = s.to_glib() as i64;
glib_sys::GTRUE
}
None => glib_sys::GFALSE,
}
}
unsafe extern "C" fn media_create_rtpbin<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> *mut gst_sys::GstElement
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
let res: *mut gst_sys::GstElement = imp.create_rtpbin(&wrap).to_glib_full();
if !res.is_null() {
gobject_sys::g_object_force_floating(res as *mut _);
}
res
}
unsafe extern "C" fn media_setup_rtpbin<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
rtpbin: *mut gst_sys::GstElement,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
// If the rtpbin was floating before make sure it is not anymore for now so
// we don't accidentally take ownership of it somewhere along the line
if gobject_sys::g_object_is_floating(rtpbin as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(rtpbin as *mut _);
}
let res = match imp.setup_rtpbin(&wrap, &from_glib_borrow(rtpbin)) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
};
// Ensure that the rtpbin is still floating afterwards here
gobject_sys::g_object_force_floating(rtpbin as *mut _);
res
}
unsafe extern "C" fn media_setup_sdp<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
sdp: *mut gst_sdp_sys::GstSDPMessage,
info: *mut gst_rtsp_server_sys::GstSDPInfo,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.setup_sdp(
&wrap,
&mut *(sdp as *mut gst_sdp::SDPMessageRef),
&SDPInfo(ptr::NonNull::new(info).expect("NULL SDPInfo")),
) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_new_stream<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
stream: *mut gst_rtsp_server_sys::GstRTSPStream,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.new_stream(&wrap, &from_glib_borrow(stream));
}
unsafe extern "C" fn media_removed_stream<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
stream: *mut gst_rtsp_server_sys::GstRTSPStream,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.removed_stream(&wrap, &from_glib_borrow(stream));
}
unsafe extern "C" fn media_prepared<T: ObjectSubclass>(ptr: *mut gst_rtsp_server_sys::GstRTSPMedia)
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.prepared(&wrap);
}
unsafe extern "C" fn media_unprepared<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.unprepared(&wrap);
}
unsafe extern "C" fn media_target_state<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
state: gst_sys::GstState,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.target_state(&wrap, from_glib(state));
}
unsafe extern "C" fn media_new_state<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
state: gst_sys::GstState,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.new_state(&wrap, from_glib(state));
}
unsafe extern "C" fn media_handle_sdp<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
sdp: *mut gst_sdp_sys::GstSDPMessage,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.handle_sdp(&wrap, &*(sdp as *const gst_sdp::SDPMessageRef)) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log();
glib_sys::GFALSE
}
}
}

View file

@ -0,0 +1,338 @@
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst_rtsp_server_sys;
use glib::translate::*;
use gst_rtsp;
use glib::subclass::prelude::*;
use RTSPMediaFactory;
use RTSPMediaFactoryClass;
pub trait RTSPMediaFactoryImpl:
RTSPMediaFactoryImplExt + ObjectImpl + Send + Sync + 'static
{
fn gen_key(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<glib::GString> {
self.parent_gen_key(factory, url)
}
fn create_element(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element> {
self.parent_create_element(factory, url)
}
fn construct(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<::RTSPMedia> {
self.parent_construct(factory, url)
}
fn create_pipeline(
&self,
factory: &RTSPMediaFactory,
media: &::RTSPMedia,
) -> Option<gst::Pipeline> {
self.parent_create_pipeline(factory, media)
}
fn configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
self.parent_configure(factory, media)
}
fn media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
self.parent_media_constructed(factory, media)
}
fn media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
self.parent_media_configure(factory, media)
}
}
pub trait RTSPMediaFactoryImplExt {
fn parent_gen_key(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<glib::GString>;
fn parent_create_element(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element>;
fn parent_construct(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<::RTSPMedia>;
fn parent_create_pipeline(
&self,
factory: &RTSPMediaFactory,
media: &::RTSPMedia,
) -> Option<gst::Pipeline>;
fn parent_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia);
fn parent_media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia);
fn parent_media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia);
}
impl<T: RTSPMediaFactoryImpl + ObjectImpl> RTSPMediaFactoryImplExt for T {
fn parent_gen_key(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<glib::GString> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.gen_key
.map(|f| from_glib_full(f(factory.to_glib_none().0, url.to_glib_none().0)))
.unwrap_or(None)
}
}
fn parent_create_element(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.create_element
.map(|f| from_glib_none(f(factory.to_glib_none().0, url.to_glib_none().0)))
.unwrap_or(None)
}
}
fn parent_construct(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<::RTSPMedia> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.construct
.map(|f| from_glib_full(f(factory.to_glib_none().0, url.to_glib_none().0)))
.unwrap_or(None)
}
}
fn parent_create_pipeline(
&self,
factory: &RTSPMediaFactory,
media: &::RTSPMedia,
) -> Option<gst::Pipeline> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.create_pipeline
.map(|f| {
let ptr = f(factory.to_glib_none().0, media.to_glib_none().0)
as *mut gst_sys::GstPipeline;
// See https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/merge_requests/109
if gobject_sys::g_object_is_floating(ptr as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(ptr as *mut _);
}
from_glib_none(ptr)
})
.unwrap_or(None)
}
}
fn parent_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
if let Some(f) = (*parent_class).configure {
f(factory.to_glib_none().0, media.to_glib_none().0);
}
}
}
fn parent_media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
if let Some(f) = (*parent_class).media_constructed {
f(factory.to_glib_none().0, media.to_glib_none().0);
}
}
}
fn parent_media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
if let Some(f) = (*parent_class).media_configure {
f(factory.to_glib_none().0, media.to_glib_none().0);
}
}
}
}
unsafe impl<T: ObjectSubclass + RTSPMediaFactoryImpl> IsSubclassable<T> for RTSPMediaFactoryClass {
fn override_vfuncs(&mut self) {
<glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass =
&mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass);
klass.gen_key = Some(factory_gen_key::<T>);
klass.create_element = Some(factory_create_element::<T>);
klass.construct = Some(factory_construct::<T>);
klass.create_pipeline = Some(factory_create_pipeline::<T>);
klass.configure = Some(factory_configure::<T>);
klass.media_constructed = Some(factory_media_constructed::<T>);
klass.media_configure = Some(factory_media_configure::<T>);
}
}
}
unsafe extern "C" fn factory_gen_key<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
url: *const gst_rtsp_sys::GstRTSPUrl,
) -> *mut std::os::raw::c_char
where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.gen_key(&wrap, &from_glib_borrow(url)).to_glib_full()
}
unsafe extern "C" fn factory_create_element<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
url: *const gst_rtsp_sys::GstRTSPUrl,
) -> *mut gst_sys::GstElement
where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
let element = imp
.create_element(&wrap, &from_glib_borrow(url))
.to_glib_full();
gobject_sys::g_object_force_floating(element as *mut _);
element
}
unsafe extern "C" fn factory_construct<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
url: *const gst_rtsp_sys::GstRTSPUrl,
) -> *mut gst_rtsp_server_sys::GstRTSPMedia
where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.construct(&wrap, &from_glib_borrow(url)).to_glib_full()
}
lazy_static! {
static ref PIPELINE_QUARK: glib::Quark =
glib::Quark::from_string("gstreamer-rs-rtsp-media-pipeline");
}
unsafe extern "C" fn factory_create_pipeline<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> *mut gst_sys::GstElement
where
T: RTSPMediaFactoryImpl,
{
use std::mem;
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
let pipeline: *mut gst_sys::GstPipeline = imp
.create_pipeline(&wrap, &from_glib_borrow(media))
.to_glib_full();
// FIXME We somehow need to ensure the pipeline actually stays alive...
gobject_sys::g_object_set_qdata_full(
media as *mut _,
PIPELINE_QUARK.to_glib(),
pipeline as *mut _,
Some(mem::transmute(gobject_sys::g_object_unref as usize)),
);
pipeline as *mut _
}
unsafe extern "C" fn factory_configure<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.configure(&wrap, &from_glib_borrow(media));
}
unsafe extern "C" fn factory_media_constructed<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.media_constructed(&wrap, &from_glib_borrow(media));
}
unsafe extern "C" fn factory_media_configure<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.media_configure(&wrap, &from_glib_borrow(media));
}

View file

@ -0,0 +1,92 @@
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst_rtsp_server_sys;
use glib::subclass::prelude::*;
use glib::translate::*;
use RTSPServer;
use RTSPServerClass;
pub trait RTSPServerImpl: RTSPServerImplExt + ObjectImpl + Send + Sync + 'static {
fn create_client(&self, server: &RTSPServer) -> Option<::RTSPClient> {
self.parent_create_client(server)
}
fn client_connected(&self, server: &RTSPServer, client: &::RTSPClient) {
self.parent_client_connected(server, client);
}
}
pub trait RTSPServerImplExt {
fn parent_create_client(&self, server: &RTSPServer) -> Option<::RTSPClient>;
fn parent_client_connected(&self, server: &RTSPServer, client: &::RTSPClient);
}
impl<T: RTSPServerImpl + ObjectImpl> RTSPServerImplExt for T {
fn parent_create_client(&self, server: &RTSPServer) -> Option<::RTSPClient> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPServerClass;
let f = (*parent_class)
.create_client
.expect("No `create_client` virtual method implementation in parent class");
from_glib_full(f(server.to_glib_none().0))
}
}
fn parent_client_connected(&self, server: &RTSPServer, client: &::RTSPClient) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPServerClass;
if let Some(f) = (*parent_class).client_connected {
f(server.to_glib_none().0, client.to_glib_none().0)
}
}
}
}
unsafe impl<T: ObjectSubclass + RTSPServerImpl> IsSubclassable<T> for RTSPServerClass {
fn override_vfuncs(&mut self) {
<glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass = &mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPServerClass);
klass.create_client = Some(server_create_client::<T>);
klass.client_connected = Some(server_client_connected::<T>);
}
}
}
unsafe extern "C" fn server_create_client<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPServer,
) -> *mut gst_rtsp_server_sys::GstRTSPClient
where
T: RTSPServerImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPServer = from_glib_borrow(ptr);
imp.create_client(&wrap).to_glib_full()
}
unsafe extern "C" fn server_client_connected<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPServer,
client: *mut gst_rtsp_server_sys::GstRTSPClient,
) where
T: RTSPServerImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPServer = from_glib_borrow(ptr);
imp.client_connected(&wrap, &from_glib_borrow(client));
}

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-rtsp"
version = "0.15.0"
version = "0.15.7"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Rtsp library"
@ -15,16 +15,16 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gio-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtsp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-sdp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { path = "../gstreamer" }
gstreamer-sdp = { path = "../gstreamer-sdp" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gio-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtsp-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-sdp-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gio = { version = "0.8" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-sdp"
version = "0.15.0"
version = "0.15.7"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Sdp library"
@ -13,12 +13,12 @@ keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"]
build = "build.rs"
[dependencies]
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-sdp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-sdp-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -386,7 +386,7 @@ impl SDPMediaRef {
}
}
pub fn insert_attribute(&mut self, idx: Option<u32>, mut attr: SDPAttribute) -> Result<(), ()> {
pub fn insert_attribute(&mut self, idx: Option<u32>, attr: SDPAttribute) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.attributes_len() {
return Err(());
@ -394,16 +394,16 @@ impl SDPMediaRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut attr = mem::ManuallyDrop::new(attr);
let result =
unsafe { gst_sdp_sys::gst_sdp_media_insert_attribute(&mut self.0, idx, &mut attr.0) };
mem::forget(attr);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn insert_bandwidth(&mut self, idx: Option<u32>, mut bw: SDPBandwidth) -> Result<(), ()> {
pub fn insert_bandwidth(&mut self, idx: Option<u32>, bw: SDPBandwidth) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.bandwidths_len() {
return Err(());
@ -411,20 +411,16 @@ impl SDPMediaRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut bw = mem::ManuallyDrop::new(bw);
let result =
unsafe { gst_sdp_sys::gst_sdp_media_insert_bandwidth(&mut self.0, idx, &mut bw.0) };
mem::forget(bw);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn insert_connection(
&mut self,
idx: Option<u32>,
mut conn: SDPConnection,
) -> Result<(), ()> {
pub fn insert_connection(&mut self, idx: Option<u32>, conn: SDPConnection) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.connections_len() {
return Err(());
@ -432,9 +428,9 @@ impl SDPMediaRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut conn = mem::ManuallyDrop::new(conn);
let result =
unsafe { gst_sdp_sys::gst_sdp_media_insert_connection(&mut self.0, idx, &mut conn.0) };
mem::forget(conn);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
@ -506,42 +502,42 @@ impl SDPMediaRef {
}
}
pub fn replace_attribute(&mut self, idx: u32, mut attr: SDPAttribute) -> Result<(), ()> {
pub fn replace_attribute(&mut self, idx: u32, attr: SDPAttribute) -> Result<(), ()> {
if idx >= self.attributes_len() {
return Err(());
}
let mut attr = mem::ManuallyDrop::new(attr);
let result =
unsafe { gst_sdp_sys::gst_sdp_media_replace_attribute(&mut self.0, idx, &mut attr.0) };
mem::forget(attr);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn replace_bandwidth(&mut self, idx: u32, mut bw: SDPBandwidth) -> Result<(), ()> {
pub fn replace_bandwidth(&mut self, idx: u32, bw: SDPBandwidth) -> Result<(), ()> {
if idx >= self.bandwidths_len() {
return Err(());
}
let mut bw = mem::ManuallyDrop::new(bw);
let result =
unsafe { gst_sdp_sys::gst_sdp_media_replace_bandwidth(&mut self.0, idx, &mut bw.0) };
mem::forget(bw);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn replace_connection(&mut self, idx: u32, mut conn: SDPConnection) -> Result<(), ()> {
pub fn replace_connection(&mut self, idx: u32, conn: SDPConnection) -> Result<(), ()> {
if idx >= self.connections_len() {
return Err(());
}
let mut conn = mem::ManuallyDrop::new(conn);
let result =
unsafe { gst_sdp_sys::gst_sdp_media_replace_connection(&mut self.0, idx, &mut conn.0) };
mem::forget(conn);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),

View file

@ -508,7 +508,7 @@ impl SDPMessageRef {
}
}
pub fn insert_attribute(&mut self, idx: Option<u32>, mut attr: SDPAttribute) -> Result<(), ()> {
pub fn insert_attribute(&mut self, idx: Option<u32>, attr: SDPAttribute) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.attributes_len() {
return Err(());
@ -516,16 +516,16 @@ impl SDPMessageRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut attr = mem::ManuallyDrop::new(attr);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_insert_attribute(&mut self.0, idx, &mut attr.0) };
mem::forget(attr);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn insert_bandwidth(&mut self, idx: Option<u32>, mut bw: SDPBandwidth) -> Result<(), ()> {
pub fn insert_bandwidth(&mut self, idx: Option<u32>, bw: SDPBandwidth) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.bandwidths_len() {
return Err(());
@ -533,9 +533,9 @@ impl SDPMessageRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut bw = mem::ManuallyDrop::new(bw);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_insert_bandwidth(&mut self.0, idx, &mut bw.0) };
mem::forget(bw);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
@ -576,7 +576,7 @@ impl SDPMessageRef {
}
}
pub fn insert_time(&mut self, idx: Option<u32>, mut time: SDPTime) -> Result<(), ()> {
pub fn insert_time(&mut self, idx: Option<u32>, time: SDPTime) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.times_len() {
return Err(());
@ -584,16 +584,16 @@ impl SDPMessageRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut time = mem::ManuallyDrop::new(time);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_insert_time(&mut self.0, idx, &mut time.0) };
mem::forget(time);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn insert_zone(&mut self, idx: Option<u32>, mut zone: SDPZone) -> Result<(), ()> {
pub fn insert_zone(&mut self, idx: Option<u32>, zone: SDPZone) -> Result<(), ()> {
if let Some(idx) = idx {
if idx >= self.zones_len() {
return Err(());
@ -601,9 +601,9 @@ impl SDPMessageRef {
}
let idx = idx.map(|idx| idx as i32).unwrap_or(-1);
let mut zone = mem::ManuallyDrop::new(zone);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_insert_zone(&mut self.0, idx, &mut zone.0) };
mem::forget(zone);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
@ -690,29 +690,29 @@ impl SDPMessageRef {
}
}
pub fn replace_attribute(&mut self, idx: u32, mut attr: SDPAttribute) -> Result<(), ()> {
pub fn replace_attribute(&mut self, idx: u32, attr: SDPAttribute) -> Result<(), ()> {
if idx >= self.attributes_len() {
return Err(());
}
let mut attr = mem::ManuallyDrop::new(attr);
let result = unsafe {
gst_sdp_sys::gst_sdp_message_replace_attribute(&mut self.0, idx, &mut attr.0)
};
mem::forget(attr);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn replace_bandwidth(&mut self, idx: u32, mut bw: SDPBandwidth) -> Result<(), ()> {
pub fn replace_bandwidth(&mut self, idx: u32, bw: SDPBandwidth) -> Result<(), ()> {
if idx >= self.bandwidths_len() {
return Err(());
}
let mut bw = mem::ManuallyDrop::new(bw);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_replace_bandwidth(&mut self.0, idx, &mut bw.0) };
mem::forget(bw);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
@ -747,28 +747,28 @@ impl SDPMessageRef {
}
}
pub fn replace_time(&mut self, idx: u32, mut time: SDPTime) -> Result<(), ()> {
pub fn replace_time(&mut self, idx: u32, time: SDPTime) -> Result<(), ()> {
if idx >= self.times_len() {
return Err(());
}
let mut time = mem::ManuallyDrop::new(time);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_replace_time(&mut self.0, idx, &mut time.0) };
mem::forget(time);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
}
}
pub fn replace_zone(&mut self, idx: u32, mut zone: SDPZone) -> Result<(), ()> {
pub fn replace_zone(&mut self, idx: u32, zone: SDPZone) -> Result<(), ()> {
if idx >= self.zones_len() {
return Err(());
}
let mut zone = mem::ManuallyDrop::new(zone);
let result =
unsafe { gst_sdp_sys::gst_sdp_message_replace_zone(&mut self.0, idx, &mut zone.0) };
mem::forget(zone);
match result {
gst_sdp_sys::GST_SDP_OK => Ok(()),
_ => Err(()),
@ -928,6 +928,45 @@ impl ToOwned for SDPMessageRef {
}
}
impl glib::types::StaticType for SDPMessageRef {
fn static_type() -> glib::types::Type {
unsafe { from_glib(gst_sdp_sys::gst_sdp_message_get_type()) }
}
}
impl<'a> glib::value::FromValueOptional<'a> for &'a SDPMessageRef {
unsafe fn from_value_optional(v: &'a glib::Value) -> Option<Self> {
let ptr = gobject_sys::g_value_get_boxed(v.to_glib_none().0);
if ptr.is_null() {
None
} else {
Some(&*(ptr as *const SDPMessageRef))
}
}
}
impl glib::value::SetValue for SDPMessageRef {
unsafe fn set_value(v: &mut glib::Value, s: &Self) {
gobject_sys::g_value_set_boxed(
v.to_glib_none_mut().0,
s as *const SDPMessageRef as glib_sys::gpointer,
);
}
}
impl glib::value::SetValueOptional for SDPMessageRef {
unsafe fn set_value_optional(v: &mut glib::Value, s: Option<&Self>) {
if let Some(s) = s {
gobject_sys::g_value_set_boxed(
v.to_glib_none_mut().0,
s as *const SDPMessageRef as glib_sys::gpointer,
);
} else {
gobject_sys::g_value_set_boxed(v.to_glib_none_mut().0, ptr::null_mut());
}
}
}
macro_rules! define_iter(
($name:ident, $typ:ty, $get_item:expr, $get_len:expr) => {
#[derive(Debug)]

View file

@ -5,6 +5,136 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.7] - 2020-06-08
### Fixed
- Allow multiple filter types per process with `gst::Iterator::filter()`.
- Check that `VideoInfo` is valid when creating a `VideoFrame`.
- Don't potentially dereference a `NULL` pointer when getting the format
from an invalid `VideoInfo` or `AudioInfo`.
- Don't unmap borrowed `VideoFrameRef`s.
### Added
- `gst::ProtectionMeta`, `gst_video::VideoAffineTransformationMeta`,
`VideoCropMeta` and `VideoRegionOfInterestMeta` bindings.
- Various new `gst_rtp::RTPBuffer` methods.
- `gst_audio::audio_buffer_truncate()`, `AudioMeta` and `AudioBuffer`
bindings.
## [0.15.6] - 2020-05-28
### Fixed
- Assert that the data passed to `VideoCaptionMeta::add()` is not empty.
- Don't store strong references to the object in the bus, appsink and appsrc
futures `Stream` / `Sink` adapters. This would keep them alive unnecessarily
and would prevent the `Stream` / `Sink` to ever "finish" on its own.
- Handle receiving a `None` reply in the change function of `gst::Promise`.
This is apparently valid. For backwards compatibility reasons this is
currently replaced with an empty structure but in 0.16 the API will
explicitly handle `None`.
### Added
- `gst::Stream::debug()` and `gst::StreamCollection::debug()` for converting
into a structured string with the actual contents of each.
- `gst::Structure::from_iter()` and `gst::Caps::from_iter()` to create
structures/caps from iterators.
- `gst::Event` support for getting/setting the `gst::Stream` in the
`StreamStart` event.
- `gst_video::calculate_display_ratio()` and `::guess_framerate()`.
- Various video related `gst::CapsFeatures` in `gst_video`.
- `TryFrom`/`From` impls for converting between `gst::Structure` and
`gst_video::VideoConverterConfig`.
- Various `glib::Value` trait impls for `SDPMessage`, `StructureRef`,
`CapsFeatureRef` and all borrowed variants of miniobjects to be able to
work with the borrowed, non-owned variants when handling `glib::Value`s.
## [0.15.5] - 2020-05-03
### Fixed
- Revert: Allow logging any `glib::Object` and not just `gst::Object`. This
broke API in subtile ways and needs to wait until 0.16
- Replace `%` in log output with `%%` to prevent accidental C formatting
- Add missing manual traits to the documentation
### Added
- `BufferRef::peek_memory_mut()` to give a mutable reference to a given memory
- Different iterators for iterating over the memories of a buffer
- Support for `gst_audio::AudioClippingMeta`
- `gst::Plugin::get_plugin_name()` was added
- `gst::Element::get_current_clock_time()` and
`gst::Element::get_current_running_time() helper functions
- `gst::State` and `StateChange` API for calculating next/previous state and
convert from/to the components of a state change
### Changed
- Use `mem::ManuallyDrop` instead of `mem::forget` everywhere
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +746,14 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.7...HEAD
[0.15.7]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.6...0.15.7
[0.15.6]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.5...0.15.6
[0.15.5]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...0.15.5
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-video"
version = "0.15.0"
version = "0.15.7"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Video library"
@ -15,15 +15,17 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-base-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-video-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-base-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-video-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
lazy_static = "1.0"
futures-channel = "0.3"
futures-util = "0.3"
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -12,6 +12,66 @@ use glib::Type;
use gobject_sys;
use gst_video_sys;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoAlphaMode {
Copy,
Set,
Mult,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoAlphaMode {
type GlibType = gst_video_sys::GstVideoAlphaMode;
fn to_glib(&self) -> gst_video_sys::GstVideoAlphaMode {
match *self {
VideoAlphaMode::Copy => gst_video_sys::GST_VIDEO_ALPHA_MODE_COPY,
VideoAlphaMode::Set => gst_video_sys::GST_VIDEO_ALPHA_MODE_SET,
VideoAlphaMode::Mult => gst_video_sys::GST_VIDEO_ALPHA_MODE_MULT,
VideoAlphaMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoAlphaMode> for VideoAlphaMode {
fn from_glib(value: gst_video_sys::GstVideoAlphaMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoAlphaMode::Copy,
1 => VideoAlphaMode::Set,
2 => VideoAlphaMode::Mult,
value => VideoAlphaMode::__Unknown(value),
}
}
}
impl StaticType for VideoAlphaMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_alpha_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoAlphaMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoAlphaMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoAlphaMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[cfg(any(feature = "v1_16", feature = "dox"))]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoCaptionType {
@ -85,6 +145,69 @@ impl SetValue for VideoCaptionType {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoChromaMode {
Full,
UpsampleOnly,
DownsampleOnly,
None,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoChromaMode {
type GlibType = gst_video_sys::GstVideoChromaMode;
fn to_glib(&self) -> gst_video_sys::GstVideoChromaMode {
match *self {
VideoChromaMode::Full => gst_video_sys::GST_VIDEO_CHROMA_MODE_FULL,
VideoChromaMode::UpsampleOnly => gst_video_sys::GST_VIDEO_CHROMA_MODE_UPSAMPLE_ONLY,
VideoChromaMode::DownsampleOnly => gst_video_sys::GST_VIDEO_CHROMA_MODE_DOWNSAMPLE_ONLY,
VideoChromaMode::None => gst_video_sys::GST_VIDEO_CHROMA_MODE_NONE,
VideoChromaMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoChromaMode> for VideoChromaMode {
fn from_glib(value: gst_video_sys::GstVideoChromaMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoChromaMode::Full,
1 => VideoChromaMode::UpsampleOnly,
2 => VideoChromaMode::DownsampleOnly,
3 => VideoChromaMode::None,
value => VideoChromaMode::__Unknown(value),
}
}
}
impl StaticType for VideoChromaMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_chroma_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoChromaMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoChromaMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoChromaMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoColorMatrix {
Unknown,
@ -247,6 +370,72 @@ impl SetValue for VideoColorPrimaries {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoDitherMethod {
None,
Verterr,
FloydSteinberg,
SierraLite,
Bayer,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoDitherMethod {
type GlibType = gst_video_sys::GstVideoDitherMethod;
fn to_glib(&self) -> gst_video_sys::GstVideoDitherMethod {
match *self {
VideoDitherMethod::None => gst_video_sys::GST_VIDEO_DITHER_NONE,
VideoDitherMethod::Verterr => gst_video_sys::GST_VIDEO_DITHER_VERTERR,
VideoDitherMethod::FloydSteinberg => gst_video_sys::GST_VIDEO_DITHER_FLOYD_STEINBERG,
VideoDitherMethod::SierraLite => gst_video_sys::GST_VIDEO_DITHER_SIERRA_LITE,
VideoDitherMethod::Bayer => gst_video_sys::GST_VIDEO_DITHER_BAYER,
VideoDitherMethod::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoDitherMethod> for VideoDitherMethod {
fn from_glib(value: gst_video_sys::GstVideoDitherMethod) -> Self {
skip_assert_initialized!();
match value {
0 => VideoDitherMethod::None,
1 => VideoDitherMethod::Verterr,
2 => VideoDitherMethod::FloydSteinberg,
3 => VideoDitherMethod::SierraLite,
4 => VideoDitherMethod::Bayer,
value => VideoDitherMethod::__Unknown(value),
}
}
}
impl StaticType for VideoDitherMethod {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_dither_method_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoDitherMethod {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoDitherMethod {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoDitherMethod {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[cfg(any(feature = "v1_12", feature = "dox"))]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoFieldOrder {
@ -625,6 +814,63 @@ impl SetValue for VideoFormat {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoGammaMode {
None,
Remap,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoGammaMode {
type GlibType = gst_video_sys::GstVideoGammaMode;
fn to_glib(&self) -> gst_video_sys::GstVideoGammaMode {
match *self {
VideoGammaMode::None => gst_video_sys::GST_VIDEO_GAMMA_MODE_NONE,
VideoGammaMode::Remap => gst_video_sys::GST_VIDEO_GAMMA_MODE_REMAP,
VideoGammaMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoGammaMode> for VideoGammaMode {
fn from_glib(value: gst_video_sys::GstVideoGammaMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoGammaMode::None,
1 => VideoGammaMode::Remap,
value => VideoGammaMode::__Unknown(value),
}
}
}
impl StaticType for VideoGammaMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_gamma_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoGammaMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoGammaMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoGammaMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoInterlaceMode {
Progressive,
@ -691,6 +937,69 @@ impl SetValue for VideoInterlaceMode {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoMatrixMode {
Full,
InputOnly,
OutputOnly,
None,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoMatrixMode {
type GlibType = gst_video_sys::GstVideoMatrixMode;
fn to_glib(&self) -> gst_video_sys::GstVideoMatrixMode {
match *self {
VideoMatrixMode::Full => gst_video_sys::GST_VIDEO_MATRIX_MODE_FULL,
VideoMatrixMode::InputOnly => gst_video_sys::GST_VIDEO_MATRIX_MODE_INPUT_ONLY,
VideoMatrixMode::OutputOnly => gst_video_sys::GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY,
VideoMatrixMode::None => gst_video_sys::GST_VIDEO_MATRIX_MODE_NONE,
VideoMatrixMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoMatrixMode> for VideoMatrixMode {
fn from_glib(value: gst_video_sys::GstVideoMatrixMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoMatrixMode::Full,
1 => VideoMatrixMode::InputOnly,
2 => VideoMatrixMode::OutputOnly,
3 => VideoMatrixMode::None,
value => VideoMatrixMode::__Unknown(value),
}
}
}
impl StaticType for VideoMatrixMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_matrix_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoMatrixMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoMatrixMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoMatrixMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoMultiviewFramePacking {
None,
@ -894,6 +1203,132 @@ impl SetValue for VideoMultiviewMode {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoPrimariesMode {
None,
MergeOnly,
Fast,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoPrimariesMode {
type GlibType = gst_video_sys::GstVideoPrimariesMode;
fn to_glib(&self) -> gst_video_sys::GstVideoPrimariesMode {
match *self {
VideoPrimariesMode::None => gst_video_sys::GST_VIDEO_PRIMARIES_MODE_NONE,
VideoPrimariesMode::MergeOnly => gst_video_sys::GST_VIDEO_PRIMARIES_MODE_MERGE_ONLY,
VideoPrimariesMode::Fast => gst_video_sys::GST_VIDEO_PRIMARIES_MODE_FAST,
VideoPrimariesMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoPrimariesMode> for VideoPrimariesMode {
fn from_glib(value: gst_video_sys::GstVideoPrimariesMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoPrimariesMode::None,
1 => VideoPrimariesMode::MergeOnly,
2 => VideoPrimariesMode::Fast,
value => VideoPrimariesMode::__Unknown(value),
}
}
}
impl StaticType for VideoPrimariesMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_primaries_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoPrimariesMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoPrimariesMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoPrimariesMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoResamplerMethod {
Nearest,
Linear,
Cubic,
Sinc,
Lanczos,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoResamplerMethod {
type GlibType = gst_video_sys::GstVideoResamplerMethod;
fn to_glib(&self) -> gst_video_sys::GstVideoResamplerMethod {
match *self {
VideoResamplerMethod::Nearest => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_NEAREST,
VideoResamplerMethod::Linear => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_LINEAR,
VideoResamplerMethod::Cubic => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_CUBIC,
VideoResamplerMethod::Sinc => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_SINC,
VideoResamplerMethod::Lanczos => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_LANCZOS,
VideoResamplerMethod::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoResamplerMethod> for VideoResamplerMethod {
fn from_glib(value: gst_video_sys::GstVideoResamplerMethod) -> Self {
skip_assert_initialized!();
match value {
0 => VideoResamplerMethod::Nearest,
1 => VideoResamplerMethod::Linear,
2 => VideoResamplerMethod::Cubic,
3 => VideoResamplerMethod::Sinc,
4 => VideoResamplerMethod::Lanczos,
value => VideoResamplerMethod::__Unknown(value),
}
}
}
impl StaticType for VideoResamplerMethod {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_resampler_method_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoResamplerMethod {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoResamplerMethod {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoResamplerMethod {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoTileMode {
Unknown,

View file

@ -21,16 +21,23 @@ pub use self::video_overlay::VideoOverlayExt;
pub use self::video_overlay::{VideoOverlay, NONE_VIDEO_OVERLAY};
mod enums;
pub use self::enums::VideoAlphaMode;
#[cfg(any(feature = "v1_16", feature = "dox"))]
pub use self::enums::VideoCaptionType;
pub use self::enums::VideoChromaMode;
pub use self::enums::VideoColorMatrix;
pub use self::enums::VideoColorPrimaries;
pub use self::enums::VideoDitherMethod;
#[cfg(any(feature = "v1_12", feature = "dox"))]
pub use self::enums::VideoFieldOrder;
pub use self::enums::VideoFormat;
pub use self::enums::VideoGammaMode;
pub use self::enums::VideoInterlaceMode;
pub use self::enums::VideoMatrixMode;
pub use self::enums::VideoMultiviewFramePacking;
pub use self::enums::VideoMultiviewMode;
pub use self::enums::VideoPrimariesMode;
pub use self::enums::VideoResamplerMethod;
pub use self::enums::VideoTileMode;
pub use self::enums::VideoTransferFunction;

View file

@ -0,0 +1,49 @@
// Copyright (C) 2020 Mathieu Duponchelle <mathieu@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst::CapsFeatures;
use gst_video_sys;
use std::ffi::CStr;
lazy_static! {
pub static ref CAPS_FEATURE_FORMAT_INTERLACED: &'static str = unsafe {
CStr::from_ptr(gst_video_sys::GST_CAPS_FEATURE_FORMAT_INTERLACED)
.to_str()
.unwrap()
};
pub static ref CAPS_FEATURES_FORMAT_INTERLACED: CapsFeatures =
CapsFeatures::new(&[*CAPS_FEATURE_FORMAT_INTERLACED]);
pub static ref CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META: &'static str = unsafe {
CStr::from_ptr(gst_video_sys::GST_CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META)
.to_str()
.unwrap()
};
pub static ref CAPS_FEATURES_META_GST_VIDEO_AFFINE_TRANSFORMATION_META: CapsFeatures =
CapsFeatures::new(&[*CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META]);
pub static ref CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META: &'static str = unsafe {
CStr::from_ptr(gst_video_sys::GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META)
.to_str()
.unwrap()
};
pub static ref CAPS_FEATURES_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META: CapsFeatures =
CapsFeatures::new(&[*CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META]);
pub static ref CAPS_FEATURE_META_GST_VIDEO_META: &'static str = unsafe {
CStr::from_ptr(gst_video_sys::GST_CAPS_FEATURE_META_GST_VIDEO_META)
.to_str()
.unwrap()
};
pub static ref CAPS_FEATURES_META_GST_VIDEO_META: CapsFeatures =
CapsFeatures::new(&[*CAPS_FEATURE_META_GST_VIDEO_META]);
pub static ref CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION: &'static str = unsafe {
CStr::from_ptr(gst_video_sys::GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION)
.to_str()
.unwrap()
};
pub static ref CAPS_FEATURES_META_GST_VIDEO_OVERLAY_COMPOSITION: CapsFeatures =
CapsFeatures::new(&[*CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION]);
}

View file

@ -11,7 +11,7 @@ use gst_sys;
use gst_video_sys;
use glib;
use glib::translate::{from_glib_full, ToGlib, ToGlibPtr};
use glib::translate::{from_glib, from_glib_full, ToGlib, ToGlibPtr};
use gst;
use std::mem;
@ -107,6 +107,90 @@ unsafe fn convert_sample_async_unsafe<F>(
);
}
pub fn convert_sample_future(
sample: &gst::Sample,
caps: &gst::Caps,
timeout: gst::ClockTime,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<gst::Sample, glib::Error>> + 'static>>
{
use futures_channel::oneshot;
use futures_util::future::lazy;
use futures_util::future::FutureExt;
let (sender, receiver) = oneshot::channel();
let sample = sample.clone();
let caps = caps.clone();
let future = lazy(move |_| {
assert!(
glib::MainContext::ref_thread_default().is_owner(),
"Spawning futures only allowed if the thread is owning the MainContext"
);
convert_sample_async(&sample, &caps, timeout, move |res| {
let _ = sender.send(res);
});
})
.then(|_| receiver.map(|res| res.expect("Sender dropped before callback was called")));
Box::pin(future)
}
pub fn calculate_display_ratio(
video_width: u32,
video_height: u32,
video_par: gst::Fraction,
display_par: gst::Fraction,
) -> Option<gst::Fraction> {
skip_assert_initialized!();
unsafe {
let mut dar_n = mem::MaybeUninit::uninit();
let mut dar_d = mem::MaybeUninit::uninit();
let res: bool = from_glib(gst_video_sys::gst_video_calculate_display_ratio(
dar_n.as_mut_ptr(),
dar_d.as_mut_ptr(),
video_width,
video_height,
*video_par.numer() as u32,
*video_par.denom() as u32,
*display_par.numer() as u32,
*display_par.denom() as u32,
));
if res {
Some(gst::Fraction::new(
dar_n.assume_init() as i32,
dar_d.assume_init() as i32,
))
} else {
None
}
}
}
pub fn guess_framerate(duration: gst::ClockTime) -> Option<gst::Fraction> {
skip_assert_initialized!();
unsafe {
let mut dest_n = mem::MaybeUninit::uninit();
let mut dest_d = mem::MaybeUninit::uninit();
let res: bool = from_glib(gst_video_sys::gst_video_guess_framerate(
duration.to_glib(),
dest_n.as_mut_ptr(),
dest_d.as_mut_ptr(),
));
if res {
Some(gst::Fraction::new(
dest_n.assume_init() as i32,
dest_d.assume_init() as i32,
))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -156,7 +240,6 @@ mod tests {
l.run();
let res = res_store.lock().unwrap().take().unwrap();
assert!(res.is_ok(), "Error {}", res.unwrap_err());
let res = res.unwrap();
let converted_out_caps = res.get_caps().unwrap();

View file

@ -18,6 +18,8 @@ extern crate glib_sys;
extern crate gobject_sys;
#[macro_use]
extern crate gstreamer as gst;
extern crate futures_channel;
extern crate futures_util;
extern crate gstreamer_base as gst_base;
extern crate gstreamer_base_sys as gst_base_sys;
extern crate gstreamer_sys as gst_sys;
@ -41,6 +43,15 @@ macro_rules! skip_assert_initialized {
mod auto;
pub use auto::*;
mod caps_features;
pub use caps_features::{
CAPS_FEATURES_FORMAT_INTERLACED, CAPS_FEATURES_META_GST_VIDEO_AFFINE_TRANSFORMATION_META,
CAPS_FEATURES_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, CAPS_FEATURES_META_GST_VIDEO_META,
CAPS_FEATURES_META_GST_VIDEO_OVERLAY_COMPOSITION, CAPS_FEATURE_FORMAT_INTERLACED,
CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META,
CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META, CAPS_FEATURE_META_GST_VIDEO_META,
CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
};
mod video_format;
pub use video_format::*;
mod video_format_info;
@ -59,8 +70,13 @@ mod video_rectangle;
pub use video_rectangle::*;
mod video_overlay_composition;
pub use video_overlay_composition::*;
mod video_meta;
pub use video_meta::*;
pub mod video_meta;
#[cfg(any(feature = "v1_16", feature = "dox"))]
pub use video_meta::VideoCaptionMeta;
pub use video_meta::{
VideoAffineTransformationMeta, VideoCropMeta, VideoMeta, VideoOverlayCompositionMeta,
VideoRegionOfInterestMeta,
};
#[cfg(any(feature = "v1_10", feature = "dox"))]
mod video_time_code;
#[cfg(any(feature = "v1_10", feature = "dox"))]
@ -75,6 +91,8 @@ pub use video_buffer_pool::{
BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META,
BUFFER_POOL_OPTION_VIDEO_META,
};
pub mod video_converter;
pub use video_converter::{VideoConverter, VideoConverterConfig};
mod video_codec_frame;
mod video_decoder;

View file

@ -58,6 +58,14 @@ pub trait VideoDecoderImpl: VideoDecoderImplExt + ElementImpl + Send + Sync + 's
self.parent_set_format(element, state)
}
fn set_format_static(
&self,
element: &VideoDecoder,
state: &VideoCodecState<'static, Readable>,
) -> Result<(), gst::LoggableError> {
self.set_format(element, state)
}
fn parse(
&self,
element: &VideoDecoder,
@ -671,7 +679,7 @@ where
let wrap_state = VideoCodecState::<Readable>::new(state);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.set_format(&wrap, &wrap_state) {
match imp.set_format_static(&wrap, &wrap_state) {
Ok(()) => true,
Err(err) => {
err.log_with_object(&wrap);

View file

@ -53,6 +53,14 @@ pub trait VideoEncoderImpl: VideoEncoderImplExt + ElementImpl + Send + Sync + 's
self.parent_set_format(element, state)
}
fn set_format_static(
&self,
element: &VideoEncoder,
state: &VideoCodecState<'static, Readable>,
) -> Result<(), gst::LoggableError> {
self.set_format(element, state)
}
fn handle_frame(
&self,
element: &VideoEncoder,
@ -588,7 +596,7 @@ where
let wrap_state = VideoCodecState::<Readable>::new(state);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.set_format(&wrap, &wrap_state) {
match imp.set_format_static(&wrap, &wrap_state) {
Ok(()) => true,
Err(err) => {
err.log_with_object(&wrap);

View file

@ -198,8 +198,8 @@ impl<'a> VideoCodecFrame<'a> {
let stream_lock = self.element.get_stream_lock();
glib_sys::g_rec_mutex_unlock(stream_lock);
let ptr = self.to_glib_none().0;
mem::forget(self);
let s = mem::ManuallyDrop::new(self);
let ptr = s.to_glib_none().0;
ptr
}

View file

@ -0,0 +1,429 @@
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use gst_video_sys;
use glib;
use glib::translate::ToGlibPtr;
use gst;
use std::convert;
use std::ops;
use std::ptr;
#[derive(Debug)]
pub struct VideoConverter(ptr::NonNull<gst_video_sys::GstVideoConverter>);
impl Drop for VideoConverter {
fn drop(&mut self) {
unsafe {
gst_video_sys::gst_video_converter_free(self.0.as_ptr());
}
}
}
unsafe impl Send for VideoConverter {}
unsafe impl Sync for VideoConverter {}
impl VideoConverter {
pub fn new(
in_info: &::VideoInfo,
out_info: &::VideoInfo,
config: Option<VideoConverterConfig>,
) -> Result<Self, glib::BoolError> {
if in_info.fps() != out_info.fps() {
return Err(glib_bool_error!("Can't do framerate conversion"));
}
if in_info.interlace_mode() != out_info.interlace_mode() {
return Err(glib_bool_error!("Can't do interlacing conversion"));
}
unsafe {
let ptr = gst_video_sys::gst_video_converter_new(
in_info.to_glib_none().0 as *mut _,
out_info.to_glib_none().0 as *mut _,
config.map(|s| s.0.into_ptr()).unwrap_or(ptr::null_mut()),
);
if ptr.is_null() {
Err(glib_bool_error!("Failed to create video converter"))
} else {
Ok(VideoConverter(ptr::NonNull::new_unchecked(ptr)))
}
}
}
pub fn get_config(&self) -> VideoConverterConfig {
unsafe {
VideoConverterConfig(
gst::StructureRef::from_glib_borrow(gst_video_sys::gst_video_converter_get_config(
self.0.as_ptr(),
))
.to_owned(),
)
}
}
pub fn set_config(&mut self, config: VideoConverterConfig) {
unsafe {
gst_video_sys::gst_video_converter_set_config(self.0.as_ptr(), config.0.into_ptr());
}
}
pub fn frame<T>(
&self,
src: &::VideoFrame<T>,
dest: &mut ::VideoFrame<::video_frame::Writable>,
) {
unsafe {
gst_video_sys::gst_video_converter_frame(
self.0.as_ptr(),
src.as_ptr(),
dest.as_mut_ptr(),
);
}
}
pub fn frame_ref<T>(
&self,
src: &::VideoFrameRef<T>,
dest: &mut ::VideoFrameRef<&mut gst::BufferRef>,
) {
unsafe {
gst_video_sys::gst_video_converter_frame(
self.0.as_ptr(),
src.as_ptr(),
dest.as_mut_ptr(),
);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VideoConverterConfig(gst::Structure);
impl ops::Deref for VideoConverterConfig {
type Target = gst::StructureRef;
fn deref(&self) -> &gst::StructureRef {
self.0.deref()
}
}
impl ops::DerefMut for VideoConverterConfig {
fn deref_mut(&mut self) -> &mut gst::StructureRef {
self.0.deref_mut()
}
}
impl AsRef<gst::StructureRef> for VideoConverterConfig {
fn as_ref(&self) -> &gst::StructureRef {
self.0.as_ref()
}
}
impl AsMut<gst::StructureRef> for VideoConverterConfig {
fn as_mut(&mut self) -> &mut gst::StructureRef {
self.0.as_mut()
}
}
impl Default for VideoConverterConfig {
fn default() -> Self {
VideoConverterConfig::new()
}
}
impl convert::TryFrom<gst::Structure> for VideoConverterConfig {
type Error = glib::BoolError;
fn try_from(v: gst::Structure) -> Result<VideoConverterConfig, Self::Error> {
skip_assert_initialized!();
if v.get_name() == "GstVideoConverter" {
Ok(VideoConverterConfig(v))
} else {
Err(glib_bool_error!("Structure is no VideoConverterConfig"))
}
}
}
impl<'a> convert::TryFrom<&'a gst::StructureRef> for VideoConverterConfig {
type Error = glib::BoolError;
fn try_from(v: &'a gst::StructureRef) -> Result<VideoConverterConfig, Self::Error> {
skip_assert_initialized!();
VideoConverterConfig::try_from(v.to_owned())
}
}
impl From<VideoConverterConfig> for gst::Structure {
fn from(v: VideoConverterConfig) -> gst::Structure {
skip_assert_initialized!();
v.0
}
}
impl VideoConverterConfig {
pub fn new() -> Self {
VideoConverterConfig(gst::Structure::new_empty("GstVideoConverter"))
}
pub fn set_resampler_method(&mut self, v: ::VideoResamplerMethod) {
self.0.set("GstVideoConverter.resampler-method", &v);
}
pub fn get_resampler_method(&self) -> ::VideoResamplerMethod {
self.0
.get_optional("GstVideoConverter.resampler-method")
.expect("Wrong type")
.unwrap_or(::VideoResamplerMethod::Cubic)
}
pub fn set_chroma_resampler_method(&mut self, v: ::VideoResamplerMethod) {
self.0.set("GstVideoConverter.chroma-resampler-method", &v);
}
pub fn get_chroma_resampler_method(&self) -> ::VideoResamplerMethod {
self.0
.get_optional("GstVideoConverter.chroma-resampler-method")
.expect("Wrong type")
.unwrap_or(::VideoResamplerMethod::Linear)
}
pub fn set_resampler_taps(&mut self, v: u32) {
self.0.set("GstVideoConverter.resampler-taps", &v);
}
pub fn get_resampler_taps(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.resampler-taps")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_dither_method(&mut self, v: ::VideoDitherMethod) {
self.0.set("GstVideoConverter.dither-method", &v);
}
pub fn get_dither_method(&self) -> ::VideoDitherMethod {
self.0
.get_optional("GstVideoConverter.dither-method")
.expect("Wrong type")
.unwrap_or(::VideoDitherMethod::Bayer)
}
pub fn set_dither_quantization(&mut self, v: u32) {
self.0.set("GstVideoConverter.dither-quantization", &v);
}
pub fn get_dither_quantization(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.dither-quantization")
.expect("Wrong type")
.unwrap_or(1)
}
pub fn set_src_x(&mut self, v: i32) {
self.0.set("GstVideoConverter.src-x", &v);
}
pub fn get_src_x(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.src-x")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_src_y(&mut self, v: i32) {
self.0.set("GstVideoConverter.src-y", &v);
}
pub fn get_src_y(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.src-y")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_src_width(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.src-width", &v);
} else {
self.0.remove_field("GstVideoConverter.src-width");
}
}
pub fn get_src_width(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.src-width")
.expect("Wrong type")
}
pub fn set_src_height(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.src-height", &v);
} else {
self.0.remove_field("GstVideoConverter.src-height");
}
}
pub fn get_src_height(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.src-height")
.expect("Wrong type")
}
pub fn set_dest_x(&mut self, v: i32) {
self.0.set("GstVideoConverter.dest-x", &v);
}
pub fn get_dest_x(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.dest-x")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_dest_y(&mut self, v: i32) {
self.0.set("GstVideoConverter.dest-y", &v);
}
pub fn get_dest_y(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.dest-y")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_dest_width(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.dest-width", &v);
} else {
self.0.remove_field("GstVideoConverter.dest-width");
}
}
pub fn get_dest_width(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.dest-width")
.expect("Wrong type")
}
pub fn set_dest_height(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.dest-height", &v);
} else {
self.0.remove_field("GstVideoConverter.dest-height");
}
}
pub fn get_dest_height(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.dest-height")
.expect("Wrong type")
}
pub fn set_fill_border(&mut self, v: bool) {
self.0.set("GstVideoConverter.fill-border", &v);
}
pub fn get_fill_border(&self) -> bool {
self.0
.get_optional("GstVideoConverter.fill-border")
.expect("Wrong type")
.unwrap_or(true)
}
pub fn set_alpha_value(&mut self, v: f64) {
self.0.set("GstVideoConverter.alpha-value", &v);
}
pub fn get_alpha_value(&self) -> f64 {
self.0
.get_optional("GstVideoConverter.alpha-value")
.expect("Wrong type")
.unwrap_or(1.0)
}
pub fn set_alpha_mode(&mut self, v: ::VideoAlphaMode) {
self.0.set("GstVideoConverter.alpha-mode", &v);
}
pub fn get_alpha_mode(&self) -> ::VideoAlphaMode {
self.0
.get_optional("GstVideoConverter.alpha-mode")
.expect("Wrong type")
.unwrap_or(::VideoAlphaMode::Copy)
}
pub fn set_border_argb(&mut self, v: u32) {
self.0.set("GstVideoConverter.border-argb", &v);
}
pub fn get_border_argb(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.border-argb")
.expect("Wrong type")
.unwrap_or(0xff_00_00_00)
}
pub fn set_chroma_mode(&mut self, v: ::VideoChromaMode) {
self.0.set("GstVideoConverter.chroma-mode", &v);
}
pub fn get_chroma_mode(&self) -> ::VideoChromaMode {
self.0
.get_optional("GstVideoConverter.chroma-mode")
.expect("Wrong type")
.unwrap_or(::VideoChromaMode::Full)
}
pub fn set_matrix_mode(&mut self, v: ::VideoMatrixMode) {
self.0.set("GstVideoConverter.matrix-mode", &v);
}
pub fn get_matrix_mode(&self) -> ::VideoMatrixMode {
self.0
.get_optional("GstVideoConverter.matrix-mode")
.expect("Wrong type")
.unwrap_or(::VideoMatrixMode::Full)
}
pub fn set_gamma_mode(&mut self, v: ::VideoGammaMode) {
self.0.set("GstVideoConverter.gamma-mode", &v);
}
pub fn get_gamma_mode(&self) -> ::VideoGammaMode {
self.0
.get_optional("GstVideoConverter.gamma-mode")
.expect("Wrong type")
.unwrap_or(::VideoGammaMode::None)
}
pub fn set_primaries_mode(&mut self, v: ::VideoPrimariesMode) {
self.0.set("GstVideoConverter.primaries-mode", &v);
}
pub fn get_primaries_mode(&self) -> ::VideoPrimariesMode {
self.0
.get_optional("GstVideoConverter.primaries-mode")
.expect("Wrong type")
.unwrap_or(::VideoPrimariesMode::None)
}
pub fn set_threads(&mut self, v: u32) {
self.0.set("GstVideoConverter.threads", &v);
}
pub fn get_threads(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.threads")
.expect("Wrong type")
.unwrap_or(1)
}
}

View file

@ -57,7 +57,7 @@ pub trait VideoDecoderExtManual: 'static {
fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime);
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime);
fn get_output_state(&self) -> Option<VideoCodecState<Readable>>;
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>>;
fn set_output_state(
&self,
fmt: VideoFormat,
@ -233,7 +233,7 @@ impl<O: IsA<VideoDecoder>> VideoDecoderExtManual for O {
}
}
fn get_output_state(&self) -> Option<VideoCodecState<Readable>> {
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>> {
let state = unsafe {
gst_video_sys::gst_video_decoder_get_output_state(self.as_ref().to_glib_none().0)
};

View file

@ -41,7 +41,7 @@ pub trait VideoEncoderExtManual: 'static {
fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime);
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime);
fn get_output_state(&self) -> Option<VideoCodecState<Readable>>;
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>>;
fn set_output_state(
&self,
caps: gst::Caps,
@ -166,7 +166,7 @@ impl<O: IsA<VideoEncoder>> VideoEncoderExtManual for O {
}
}
fn get_output_state(&self) -> Option<VideoCodecState<Readable>> {
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>> {
let state = unsafe {
gst_video_sys::gst_video_encoder_get_output_state(self.as_ref().to_glib_none().0)
};

View file

@ -42,7 +42,10 @@ macro_rules! event_builder_generic_impl {
pub fn other_fields(self, other_fields: &[(&'a str, &'a dyn ToSendValue)]) -> Self {
Self {
other_fields: self.other_fields.iter().cloned()
other_fields: self
.other_fields
.iter()
.cloned()
.chain(other_fields.iter().cloned())
.collect(),
..self
@ -63,7 +66,7 @@ macro_rules! event_builder_generic_impl {
{
let s = gst::StructureRef::from_glib_borrow_mut(
gst_sys::gst_event_writable_structure(event)
gst_sys::gst_event_writable_structure(event),
);
for (k, v) in self.other_fields {
@ -74,7 +77,7 @@ macro_rules! event_builder_generic_impl {
from_glib_full(event)
}
}
}
};
}
pub fn new_downstream_force_key_unit_event<'a>() -> DownstreamForceKeyUnitEventBuilder<'a> {

View file

@ -14,6 +14,7 @@ use glib::translate::{from_glib, ToGlibPtr};
use gst;
use gst::miniobject::MiniObject;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ops;
@ -23,37 +24,50 @@ use std::slice;
pub enum Readable {}
pub enum Writable {}
#[derive(Debug)]
pub struct VideoFrame<T>(
gst_video_sys::GstVideoFrame,
Option<gst::Buffer>,
::VideoInfo,
PhantomData<T>,
);
pub struct VideoFrame<T> {
frame: gst_video_sys::GstVideoFrame,
buffer: Option<gst::Buffer>,
info: ::VideoInfo,
phantom: PhantomData<T>,
}
unsafe impl<T> Send for VideoFrame<T> {}
unsafe impl<T> Sync for VideoFrame<T> {}
impl<T> fmt::Debug for VideoFrame<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("VideoFrame")
.field("frame", &self.frame)
.field("buffer", &self.buffer)
.field("info", &self.info)
.field("phantom", &self.phantom)
.finish()
}
}
impl<T> VideoFrame<T> {
pub fn info(&self) -> &::VideoInfo {
&self.2
&self.info
}
pub fn flags(&self) -> ::VideoFrameFlags {
from_glib(self.0.flags)
from_glib(self.frame.flags)
}
pub fn id(&self) -> i32 {
self.0.id
self.frame.id
}
pub fn into_buffer(mut self) -> gst::Buffer {
self.1.take().unwrap()
self.buffer.take().unwrap()
}
pub fn copy(&self, dest: &mut VideoFrame<Writable>) -> Result<(), glib::BoolError> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(&mut dest.0, &self.0));
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(
&mut dest.frame,
&self.frame,
));
if res {
Ok(())
} else {
@ -71,8 +85,8 @@ impl<T> VideoFrame<T> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy_plane(
&mut dest.0,
&self.0,
&mut dest.frame,
&self.frame,
plane,
));
if res {
@ -136,7 +150,7 @@ impl<T> VideoFrame<T> {
}
pub fn buffer(&self) -> &gst::BufferRef {
unsafe { gst::BufferRef::from_ptr(self.0.buffer) }
unsafe { gst::BufferRef::from_ptr(self.frame.buffer) }
}
pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> {
@ -149,7 +163,10 @@ impl<T> VideoFrame<T> {
// Just get the palette
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts(self.0.data[1] as *const u8, 256 * 4));
return Ok(slice::from_raw_parts(
self.frame.data[1] as *const u8,
256 * 4,
));
}
}
@ -160,7 +177,7 @@ impl<T> VideoFrame<T> {
unsafe {
Ok(slice::from_raw_parts(
self.0.data[plane as usize] as *const u8,
self.frame.data[plane as usize] as *const u8,
(w * h) as usize,
))
}
@ -169,14 +186,34 @@ impl<T> VideoFrame<T> {
pub unsafe fn from_glib_full(frame: gst_video_sys::GstVideoFrame) -> Self {
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::Buffer::from_glib_none(frame.buffer);
VideoFrame(frame, Some(buffer), info, PhantomData)
VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
}
}
pub fn as_video_frame_ref(&self) -> VideoFrameRef<&gst::BufferRef> {
let frame = unsafe { ptr::read(&self.frame) };
let info = self.info.clone();
VideoFrameRef {
frame,
buffer: Some(self.buffer()),
info,
borrowed: true,
}
}
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.frame
}
}
impl<T> Drop for VideoFrame<T> {
fn drop(&mut self) {
unsafe {
gst_video_sys::gst_video_frame_unmap(&mut self.0);
gst_video_sys::gst_video_frame_unmap(&mut self.frame);
}
}
}
@ -188,6 +225,8 @@ impl VideoFrame<Readable> {
) -> Result<VideoFrame<Readable>, gst::Buffer> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
@ -202,7 +241,12 @@ impl VideoFrame<Readable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
@ -214,6 +258,8 @@ impl VideoFrame<Readable> {
) -> Result<VideoFrame<Readable>, gst::Buffer> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
@ -229,20 +275,15 @@ impl VideoFrame<Readable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
pub fn as_video_frame_ref(&self) -> VideoFrameRef<&gst::BufferRef> {
let vframe = unsafe { ptr::read(&self.0) };
let info = self.2.clone();
VideoFrameRef(vframe, Some(self.buffer()), info, true)
}
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.0
}
}
impl VideoFrame<Writable> {
@ -252,6 +293,8 @@ impl VideoFrame<Writable> {
) -> Result<VideoFrame<Writable>, gst::Buffer> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
@ -268,7 +311,12 @@ impl VideoFrame<Writable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
@ -280,6 +328,8 @@ impl VideoFrame<Writable> {
) -> Result<VideoFrame<Writable>, gst::Buffer> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
@ -297,13 +347,18 @@ impl VideoFrame<Writable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
unsafe { gst::BufferRef::from_mut_ptr(self.0.buffer) }
unsafe { gst::BufferRef::from_mut_ptr(self.frame.buffer) }
}
pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> {
@ -317,7 +372,7 @@ impl VideoFrame<Writable> {
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts_mut(
self.0.data[1] as *mut u8,
self.frame.data[1] as *mut u8,
256 * 4,
));
}
@ -330,102 +385,47 @@ impl VideoFrame<Writable> {
unsafe {
Ok(slice::from_raw_parts_mut(
self.0.data[plane as usize] as *mut u8,
self.frame.data[plane as usize] as *mut u8,
(w * h) as usize,
))
}
}
pub fn as_mut_video_frame_ref(&mut self) -> VideoFrameRef<&mut gst::BufferRef> {
let vframe = unsafe { ptr::read(&self.0) };
let info = self.2.clone();
VideoFrameRef(vframe, Some(self.buffer_mut()), info, true)
let frame = unsafe { ptr::read(&self.frame) };
let info = self.info.clone();
VideoFrameRef {
frame,
buffer: Some(self.buffer_mut()),
info,
borrowed: true,
}
}
pub fn as_mut_ptr(&mut self) -> *mut gst_video_sys::GstVideoFrame {
&mut self.0
&mut self.frame
}
}
#[derive(Debug)]
pub struct VideoFrameRef<T>(gst_video_sys::GstVideoFrame, Option<T>, ::VideoInfo, bool);
impl<'a> VideoFrameRef<&'a gst::BufferRef> {
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.0
}
pub unsafe fn from_glib_borrow(frame: *const gst_video_sys::GstVideoFrame) -> Self {
assert!(!frame.is_null());
let frame = ptr::read(frame);
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::BufferRef::from_ptr(frame.buffer);
VideoFrameRef(frame, Some(buffer), info, false)
}
pub fn from_buffer_ref_readable<'b>(
buffer: &'a gst::BufferRef,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
}
}
}
pub fn from_buffer_ref_id_readable<'b>(
buffer: &'a gst::BufferRef,
id: i32,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
id,
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
}
}
pub struct VideoFrameRef<T> {
frame: gst_video_sys::GstVideoFrame,
buffer: Option<T>,
info: ::VideoInfo,
borrowed: bool,
}
impl<T> VideoFrameRef<T> {
pub fn info(&self) -> &::VideoInfo {
&self.2
&self.info
}
pub fn flags(&self) -> ::VideoFrameFlags {
from_glib(self.0.flags)
from_glib(self.frame.flags)
}
pub fn id(&self) -> i32 {
self.0.id
self.frame.id
}
pub fn copy(
@ -433,7 +433,10 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
dest: &mut VideoFrameRef<&mut gst::BufferRef>,
) -> Result<(), glib::BoolError> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(&mut dest.0, &self.0));
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(
&mut dest.frame,
&self.frame,
));
if res {
Ok(())
} else {
@ -451,8 +454,8 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy_plane(
&mut dest.0,
&self.0,
&mut dest.frame,
&self.frame,
plane,
));
if res {
@ -515,10 +518,6 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
self.info().offset()
}
pub fn buffer(&self) -> &gst::BufferRef {
self.1.as_ref().unwrap()
}
pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> {
if plane >= self.n_planes() {
return Err(glib_bool_error!("Plane index higher than number of planes"));
@ -529,7 +528,10 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
// Just get the palette
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts(self.0.data[1] as *const u8, 256 * 4));
return Ok(slice::from_raw_parts(
self.frame.data[1] as *const u8,
256 * 4,
));
}
}
@ -540,11 +542,101 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
unsafe {
Ok(slice::from_raw_parts(
self.0.data[plane as usize] as *const u8,
self.frame.data[plane as usize] as *const u8,
(w * h) as usize,
))
}
}
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.frame
}
}
impl<'a> VideoFrameRef<&'a gst::BufferRef> {
pub unsafe fn from_glib_borrow(frame: *const gst_video_sys::GstVideoFrame) -> Self {
assert!(!frame.is_null());
let frame = ptr::read(frame);
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::BufferRef::from_ptr(frame.buffer);
VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: true,
}
}
pub fn from_buffer_ref_readable<'b>(
buffer: &'a gst::BufferRef,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
pub fn from_buffer_ref_id_readable<'b>(
buffer: &'a gst::BufferRef,
id: i32,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
id,
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
pub fn buffer(&self) -> &gst::BufferRef {
self.buffer.as_ref().unwrap()
}
}
impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
@ -554,7 +646,12 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
let frame = ptr::read(frame);
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::BufferRef::from_mut_ptr(frame.buffer);
VideoFrameRef(frame, Some(buffer), info, false)
VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: true,
}
}
pub fn from_buffer_ref_writable<'b>(
@ -563,6 +660,8 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
) -> Result<VideoFrameRef<&'a mut gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
@ -579,7 +678,12 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
@ -591,6 +695,8 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
) -> Result<VideoFrameRef<&'a mut gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
assert!(info.is_valid());
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
@ -608,13 +714,18 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
self.1.as_mut().unwrap()
self.buffer.as_mut().unwrap()
}
pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> {
@ -628,7 +739,7 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts_mut(
self.0.data[1] as *mut u8,
self.frame.data[1] as *mut u8,
256 * 4,
));
}
@ -641,14 +752,14 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
unsafe {
Ok(slice::from_raw_parts_mut(
self.0.data[plane as usize] as *mut u8,
self.frame.data[plane as usize] as *mut u8,
(w * h) as usize,
))
}
}
pub fn as_mut_ptr(&mut self) -> *mut gst_video_sys::GstVideoFrame {
&mut self.0
&mut self.frame
}
}
@ -668,9 +779,9 @@ unsafe impl<T> Sync for VideoFrameRef<T> {}
impl<T> Drop for VideoFrameRef<T> {
fn drop(&mut self) {
if !self.3 {
if !self.borrowed {
unsafe {
gst_video_sys::gst_video_frame_unmap(&mut self.0);
gst_video_sys::gst_video_frame_unmap(&mut self.frame);
}
}
}

View file

@ -267,7 +267,38 @@ impl<'a> VideoInfoBuilder<'a> {
unsafe {
let mut info = mem::MaybeUninit::uninit();
#[cfg(not(feature = "v1_16"))]
#[cfg(not(feature = "v1_12"))]
let res: bool = {
// The bool return value is new with 1.11.1, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/commit/17cdd369e6f2f73329d27dfceb50011f40f1ceb0
let res = if gst::version() < (1, 11, 1, 0) {
gst_video_sys::gst_video_info_set_format(
info.as_mut_ptr(),
self.format.to_glib(),
self.width,
self.height,
);
true
} else {
from_glib(gst_video_sys::gst_video_info_set_format(
info.as_mut_ptr(),
self.format.to_glib(),
self.width,
self.height,
))
};
if res {
if let Some(interlace_mode) = self.interlace_mode {
let info = info.as_mut_ptr();
(*info).interlace_mode = interlace_mode.to_glib();
}
}
res
};
#[cfg(all(feature = "v1_12", not(feature = "v1_16")))]
let res: bool = {
let res = from_glib(gst_video_sys::gst_video_info_set_format(
info.as_mut_ptr(),
@ -526,6 +557,10 @@ impl VideoInfo {
}
}
pub fn is_valid(&self) -> bool {
!self.0.finfo.is_null() && self.0.width > 0 && self.0.height > 0 && self.0.size > 0
}
pub fn from_caps(caps: &gst::CapsRef) -> Result<Self, glib::error::BoolError> {
skip_assert_initialized!();
@ -555,6 +590,10 @@ impl VideoInfo {
}
pub fn format(&self) -> ::VideoFormat {
if self.0.finfo.is_null() {
return ::VideoFormat::Unknown;
}
unsafe { from_glib((*self.0.finfo).format) }
}
@ -710,8 +749,23 @@ impl VideoInfo {
}
}
#[cfg(any(feature = "v1_12", feature = "dox"))]
pub fn align(&mut self, align: &mut ::VideoAlignment) -> bool {
#[cfg(not(feature = "v1_12"))]
unsafe {
// The bool return value is new with 1.11.1, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/commit/17cdd369e6f2f73329d27dfceb50011f40f1ceb0
if gst::version() < (1, 11, 1, 0) {
gst_video_sys::gst_video_info_align(&mut self.0, &mut align.0);
true
} else {
from_glib(gst_video_sys::gst_video_info_align(
&mut self.0,
&mut align.0,
))
}
}
#[cfg(feature = "v1_12")]
unsafe {
from_glib(gst_video_sys::gst_video_info_align(
&mut self.0,
@ -973,13 +1027,13 @@ mod tests {
.expect("Failed to create VideoInfo");
assert_eq!(info.stride(), [1920, 1920]);
assert_eq!(info.offset(), [0, 2073600]);
assert_eq!(info.offset(), [0, 2_073_600]);
let mut align = ::VideoAlignment::new(0, 0, 0, 8, &[0; VIDEO_MAX_PLANES]);
assert!(info.align(&mut align));
assert_eq!(info.stride(), [1928, 1928]);
assert_eq!(info.offset(), [0, 2082240]);
assert_eq!(info.offset(), [0, 2_082_240]);
}
#[cfg(any(feature = "v1_12", feature = "dox"))]

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