forked from mirrors/gstreamer-rs
Compare commits
100 commits
Author | SHA1 | Date | |
---|---|---|---|
|
dd745d2f18 | ||
|
604d58e153 | ||
|
d465fa0140 | ||
|
04a4d0a380 | ||
|
1924d3b222 | ||
|
d6b5126c87 | ||
|
71bb12564f | ||
|
ed0f1140b8 | ||
|
997dad0f11 | ||
|
de0f228075 | ||
|
51024345f8 | ||
|
2c4de19067 | ||
|
afe2b18018 | ||
|
0feadec9fe | ||
|
06505ad74d | ||
|
2868bb3033 | ||
|
570c1d014a | ||
|
d21953ba66 | ||
|
c2acb000b1 | ||
|
8e96888c63 | ||
|
487970f620 | ||
|
12063ec83a | ||
|
c35a7caf42 | ||
|
b9d1669c0d | ||
|
122ccafd1a | ||
|
215dbe53c6 | ||
|
605898277f | ||
|
b724e0c46c | ||
|
d623bcdce5 | ||
|
43e7f9f589 | ||
|
4a8b904c6c | ||
|
a1fbb359ea | ||
|
451c198cec | ||
|
ef012b9445 | ||
|
923daa04b9 | ||
|
774281a0c9 | ||
|
fdf56b1235 | ||
|
d23d313b39 | ||
|
c8ac89ec2b | ||
|
96c83bc0e6 | ||
|
26beed7277 | ||
|
d9a06b31de | ||
|
5479b23eb9 | ||
|
973ac7344e | ||
|
2bbfbb5842 | ||
|
0749250743 | ||
|
e8c5f5fb6b | ||
|
8077b7ac82 | ||
|
ef77a5ae92 | ||
|
079095b55c | ||
|
2599acc681 | ||
|
671605d8ac | ||
|
01da01c9e6 | ||
|
4e30798ac7 | ||
|
ed80467ff6 | ||
|
19295f75b5 | ||
|
e02ec7a8c9 | ||
|
21ffeb5e82 | ||
|
aa86c804bd | ||
|
38071f1897 | ||
|
d429fad50d | ||
|
67e6afc628 | ||
|
0e69898faf | ||
|
45b7676d02 | ||
|
315a59e47d | ||
|
3b56f470e5 | ||
|
0d34e70e3e | ||
|
7a05ff52af | ||
|
66338881e8 | ||
|
5beb871419 | ||
|
462f4ac3b8 | ||
|
a61c50c21a | ||
|
e6d3ac4e4b | ||
|
89b9dc80dc | ||
|
9b593f626b | ||
|
605d4fbf24 | ||
|
7fa810873f | ||
|
0ffd86c37e | ||
|
ea38e022be | ||
|
23fdd2c5ad | ||
|
3ae8b48400 | ||
|
188ef1e242 | ||
|
a47a6ea76c | ||
|
e5397f5a33 | ||
|
41663f15ef | ||
|
8a9be64c3f | ||
|
3a8ae5ddd4 | ||
|
fc1e909925 | ||
|
5577e8a457 | ||
|
456ad9fb4a | ||
|
b127f93cb9 | ||
|
db1c341cdf | ||
|
c29a7638d3 | ||
|
7f07bac0c7 | ||
|
3425bcfe9d | ||
|
28cc573875 | ||
|
fedf4b664a | ||
|
d2e508eca1 | ||
|
d62d788630 | ||
|
6fd13cc807 |
145 changed files with 11178 additions and 789 deletions
|
@ -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'
|
||||
|
|
32
Gir_Gst.toml
32
Gir_Gst.toml
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -272,6 +272,7 @@ status = "generate"
|
|||
[[object]]
|
||||
name = "GES.TimelineElement"
|
||||
status = "generate"
|
||||
manual_traits = ["TimelineElementExtManual"]
|
||||
[[object.function]]
|
||||
name = "ripple"
|
||||
[object.function.return]
|
||||
|
|
|
@ -94,6 +94,7 @@ ref_mode = "ref"
|
|||
[[object]]
|
||||
name = "GstGL.GLContext"
|
||||
status = "generate"
|
||||
manual_traits = ["GLContextExtManual"]
|
||||
|
||||
[[object.function]]
|
||||
name = "new_wrapped"
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -144,7 +144,6 @@ fn create_ui(app: >k::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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
481
examples/src/bin/rtsp-server-subclass.rs
Normal file
481
examples/src/bin/rtsp-server-subclass.rs
Normal 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),
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
2
gir
|
@ -1 +1 @@
|
|||
Subproject commit 58365737247ee2b409c9ccfe689654aa987413d5
|
||||
Subproject commit 2abca1147143e5f5986f14bf6bc794e438bef193
|
|
@ -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
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
765
gstreamer-audio/src/audio_buffer.rs
Normal file
765
gstreamer-audio/src/audio_buffer.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) }
|
||||
}
|
||||
|
||||
|
|
251
gstreamer-audio/src/audio_meta.rs
Normal file
251
gstreamer-audio/src/audio_meta.rs
Normal 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)))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
47
gstreamer-audio/src/functions.rs
Normal file
47
gstreamer-audio/src/functions.rs
Normal 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),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 _)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 _)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 _)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ impl UniqueFlowCombiner {
|
|||
self.0.add_pad(pad);
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
196
gstreamer-rtp/README.md
Normal 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.
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
|
27
gstreamer-rtsp-server/src/rtsp_media.rs
Normal file
27
gstreamer-rtsp-server/src/rtsp_media.rs
Normal 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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
gstreamer-rtsp-server/src/rtsp_thread.rs
Normal file
47
gstreamer-rtsp-server/src/rtsp_thread.rs
Normal 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_) }
|
||||
}
|
||||
}
|
20
gstreamer-rtsp-server/src/subclass/mod.rs
Normal file
20
gstreamer-rtsp-server/src/subclass/mod.rs
Normal 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};
|
||||
}
|
1284
gstreamer-rtsp-server/src/subclass/rtsp_client.rs
Normal file
1284
gstreamer-rtsp-server/src/subclass/rtsp_client.rs
Normal file
File diff suppressed because it is too large
Load diff
780
gstreamer-rtsp-server/src/subclass/rtsp_media.rs
Normal file
780
gstreamer-rtsp-server/src/subclass/rtsp_media.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
338
gstreamer-rtsp-server/src/subclass/rtsp_media_factory.rs
Normal file
338
gstreamer-rtsp-server/src/subclass/rtsp_media_factory.rs
Normal 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));
|
||||
}
|
92
gstreamer-rtsp-server/src/subclass/rtsp_server.rs
Normal file
92
gstreamer-rtsp-server/src/subclass/rtsp_server.rs
Normal 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));
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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(()),
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
49
gstreamer-video/src/caps_features.rs
Normal file
49
gstreamer-video/src/caps_features.rs
Normal 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]);
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
429
gstreamer-video/src/video_converter.rs
Normal file
429
gstreamer-video/src/video_converter.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue