mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-22 03:21:00 +00:00
tracers: Add a tracer that dumps data flow into .pcap files
See documentation for more details Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/879>
This commit is contained in:
parent
86039dd5c1
commit
a05ab37b49
6 changed files with 445 additions and 34 deletions
133
Cargo.lock
generated
133
Cargo.lock
generated
|
@ -177,7 +177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -219,7 +219,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -241,7 +241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -258,7 +258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -1132,7 +1132,7 @@ checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
|||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -1416,7 +1416,7 @@ dependencies = [
|
|||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"strsim 0.11.1",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
@ -1428,7 +1428,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -1520,6 +1520,16 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive-into-owned"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "576fce04d31d592013a5887ba8d9c3830adff329e5096d7e1eb5e8e61262ca62"
|
||||
dependencies = [
|
||||
"quote 0.3.15",
|
||||
"syn 0.11.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
|
@ -1677,6 +1687,15 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "etherparse"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "095ab548cf452be5813424558a18af88f0a620d0f4a3d8793aa09311a3b6fa5f"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.3.1"
|
||||
|
@ -1885,7 +1904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -2147,7 +2166,7 @@ dependencies = [
|
|||
"heck 0.5.0",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -2938,9 +2957,12 @@ name = "gst-plugin-tracers"
|
|||
version = "0.14.0-alpha.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"atomic_refcell",
|
||||
"etherparse",
|
||||
"gst-plugin-version-helper",
|
||||
"gstreamer",
|
||||
"once_cell",
|
||||
"pcap-file",
|
||||
"regex",
|
||||
"signal-hook",
|
||||
]
|
||||
|
@ -3569,7 +3591,7 @@ source = "git+https://github.com/gtk-rs/gtk4-rs?branch=master#f0f553dcee3862a889
|
|||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -3870,7 +3892,7 @@ dependencies = [
|
|||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2 0.4.10",
|
||||
"socket2 0.5.7",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
@ -4137,7 +4159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -4882,7 +4904,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -4979,7 +5001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -5176,6 +5198,17 @@ dependencies = [
|
|||
"hmac 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pcap-file"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ad13fed1a83120159aea81b265074f21d753d157dd16b10cc3790ecba40a341"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"derive-into-owned",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.4"
|
||||
|
@ -5218,7 +5251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -5352,7 +5385,7 @@ checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
|||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
@ -5364,7 +5397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
|
@ -5392,7 +5425,7 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -5436,7 +5469,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -5558,6 +5591,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
|
@ -6249,7 +6288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -6321,7 +6360,7 @@ checksum = "b80d3d6b56b64335c0180e5ffde23b3c5e08c14c585b51a15bd0e95393f46703"
|
|||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -6346,7 +6385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -6450,7 +6489,7 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6561,6 +6600,17 @@ version = "2.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.11.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
|
||||
dependencies = [
|
||||
"quote 0.3.15",
|
||||
"synom",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -6578,7 +6628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
|
@ -6594,6 +6644,15 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
||||
|
||||
[[package]]
|
||||
name = "synom"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
|
@ -6685,7 +6744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -6697,7 +6756,7 @@ checksum = "5a3a0c1b477619de2a1bf72990195561a06f7b68bbf272cea676236ad7cfb9e8"
|
|||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"regex",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
@ -6730,7 +6789,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -6826,7 +6885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -7002,7 +7061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -7168,6 +7227,12 @@ version = "0.1.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
|
@ -7359,7 +7424,7 @@ dependencies = [
|
|||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -7382,7 +7447,7 @@ version = "0.2.92"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
|
@ -7393,7 +7458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
|
@ -7720,7 +7785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
@ -7731,7 +7796,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"quote 1.0.36",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
|
|
|
@ -9603,6 +9603,7 @@
|
|||
"tracers": {
|
||||
"buffer-lateness": {},
|
||||
"pad-push-timings": {},
|
||||
"pcap-writer": {},
|
||||
"pipeline-snapshot": {},
|
||||
"queue-levels": {}
|
||||
},
|
||||
|
|
|
@ -13,6 +13,9 @@ gst.workspace = true
|
|||
anyhow = "1"
|
||||
regex = "1"
|
||||
once_cell.workspace = true
|
||||
atomic_refcell = "0.1"
|
||||
pcap-file = "1.1.1"
|
||||
etherparse = "0.14.0"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
signal-hook = "0.3"
|
||||
|
|
|
@ -16,6 +16,7 @@ use gst::glib;
|
|||
|
||||
mod buffer_lateness;
|
||||
mod pad_push_timings;
|
||||
mod pcap_writer;
|
||||
#[cfg(unix)]
|
||||
mod pipeline_snapshot;
|
||||
mod queue_levels;
|
||||
|
@ -26,6 +27,7 @@ fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|||
queue_levels::register(plugin)?;
|
||||
buffer_lateness::register(plugin)?;
|
||||
pad_push_timings::register(plugin)?;
|
||||
pcap_writer::register(plugin)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
320
utils/tracers/src/pcap_writer/imp.rs
Normal file
320
utils/tracers/src/pcap_writer/imp.rs
Normal file
|
@ -0,0 +1,320 @@
|
|||
// Copyright (C) 2022 Thibault Saunier <tsaunier@igalia.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/**
|
||||
* tracer-pcap-writer:
|
||||
* @short_description: Dumps pad dataflow into a `.pcap` file.
|
||||
*
|
||||
* This tracer provides an easy way to save data flowing in a specified pad using
|
||||
* the [`pcap`](https://wiki.wireshark.org/Development/LibpcapFileFormat) format.
|
||||
*
|
||||
* It can also wrap the dataflow inside fake IpV4/UDP headers so those pcap can
|
||||
* then be reused with the #pcapparse element.
|
||||
*
|
||||
* It can be used to easily record/replay some parts of a WebRTC session without
|
||||
* requiring to modify the GStreamer pipeline.
|
||||
*
|
||||
* ## Parameters:
|
||||
*
|
||||
* - `output-dir` (string, default: "tracer_pcaps"): The directory where to save
|
||||
* `.pcap` files
|
||||
* - `fake-protocol` (['udp', 'none'], default: "udp"): the fake headers to add
|
||||
* to packets
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* ```console
|
||||
* $ GST_TRACERS=pcap-writer(target-factory=rtph264pay) gst-launch-1.0 videotestsrc num-buffers=15 ! x264enc tune=zerolatency ! video/x-h264,profile=constrained-baseline ! rtph264pay seqnum-offset=0 ! rtph264depay ! fakesink
|
||||
* ```
|
||||
*
|
||||
* Then this can be replayed with:
|
||||
*
|
||||
* ```console
|
||||
* $ gst-launch-1.0 filesrc location="tracer_pcaps/pipeline0>rtph264pay0>src.pcap" ! pcapparse ! \
|
||||
* "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264, profile-level-id=(string)42c015, a-framerate=(string)30" ! \
|
||||
* rtph264depay ! avdec_h264 ! fakesink silent=false -v -m
|
||||
* ```
|
||||
*
|
||||
* Since: plugins-rs-0.13.0
|
||||
*/
|
||||
use pcap_file::pcap;
|
||||
|
||||
use etherparse::PacketBuilder;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::{create_dir_all, File};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use gst::glib;
|
||||
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
static MAX_PACKET_LEN: usize = 65535;
|
||||
static MAX_FAKE_HEADERS_LEN: usize = 54;
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"pcap-writer",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("pcap writer tracer"),
|
||||
)
|
||||
});
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Settings {
|
||||
output_dir: PathBuf,
|
||||
target_factory: Option<String>,
|
||||
pad_path: Option<String>,
|
||||
fake_protocol: FakeProtocol,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Enum)]
|
||||
#[enum_type(name = "GstPcapWriterFakeProtocol")]
|
||||
pub enum FakeProtocol {
|
||||
Udp,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
output_dir: Path::new("tracer_pcaps").to_path_buf(),
|
||||
target_factory: None,
|
||||
pad_path: None,
|
||||
fake_protocol: FakeProtocol::Udp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn update_from_params(&mut self, obj: &super::PcapWriter, params: String) {
|
||||
let s = match gst::Structure::from_str(&format!("pcap-writer-settings,{}", params)) {
|
||||
Ok(s) => s,
|
||||
Err(err) => {
|
||||
gst::warning!(CAT, obj = obj, "failed to parse tracer parameters: {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(output_dir) = s.get::<&str>("output-dir") {
|
||||
gst::log!(CAT, obj = obj, "pcap dumpdir= {}", output_dir);
|
||||
self.output_dir = Path::new(output_dir).to_path_buf();
|
||||
}
|
||||
|
||||
self.pad_path = s.get("pad-path").ok();
|
||||
if let Ok(protocol) = s.get("fake-protocol") {
|
||||
match glib::Value::deserialize(protocol, FakeProtocol::static_type()) {
|
||||
Ok(protocol) => self.fake_protocol = protocol.get().unwrap(),
|
||||
Err(err) => {
|
||||
gst::warning!(CAT, obj = obj, "failed to parse fake-protocol: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.target_factory = s.get("target-factory").ok();
|
||||
}
|
||||
}
|
||||
|
||||
struct Writer {
|
||||
writer: pcap::PcapWriter<File>,
|
||||
buf: Vec<u8>,
|
||||
protocol: FakeProtocol,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
fn new(file: File, protocol: FakeProtocol) -> Self {
|
||||
Self {
|
||||
writer: pcap::PcapWriter::new(file).expect("Error writing pcap"),
|
||||
buf: Vec::<u8>::with_capacity(MAX_PACKET_LEN + MAX_FAKE_HEADERS_LEN),
|
||||
protocol,
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, buffer: &gst::BufferRef) -> Result<(), anyhow::Error> {
|
||||
if buffer.size() > MAX_PACKET_LEN {
|
||||
anyhow::bail!("Maximum size of packet is {MAX_PACKET_LEN}");
|
||||
}
|
||||
|
||||
let map = buffer.map_readable()?;
|
||||
if matches!(self.protocol, FakeProtocol::None) {
|
||||
self.writer.write(
|
||||
0,
|
||||
buffer
|
||||
.pts()
|
||||
.unwrap_or(gst::ClockTime::from_seconds(0))
|
||||
.nseconds() as u32,
|
||||
map.as_slice(),
|
||||
map.len() as u32,
|
||||
)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
/* Add fake Ethernet/IP/UDP encapsulation for this packet */
|
||||
/* FIXME: Make Ips configurable? */
|
||||
let builder = PacketBuilder::ethernet2([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12])
|
||||
.ipv4([192, 168, 1, 1], [192, 168, 1, 2], 20)
|
||||
.udp(21, 1234);
|
||||
|
||||
let size = builder.size(map.size());
|
||||
|
||||
self.buf.clear();
|
||||
builder.write(&mut self.buf, map.as_slice()).unwrap();
|
||||
|
||||
self.writer.write(
|
||||
0,
|
||||
buffer
|
||||
.pts()
|
||||
.unwrap_or(gst::ClockTime::from_seconds(0))
|
||||
.nseconds() as u32,
|
||||
&self.buf,
|
||||
size as u32,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PcapWriter {
|
||||
settings: AtomicRefCell<Settings>,
|
||||
|
||||
pads: Mutex<HashMap<usize, Arc<Mutex<Writer>>>>,
|
||||
|
||||
ignored: Mutex<HashSet<usize>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for PcapWriter {
|
||||
const NAME: &'static str = "GstPcapWriter";
|
||||
type Type = super::PcapWriter;
|
||||
type ParentType = gst::Tracer;
|
||||
}
|
||||
|
||||
impl ObjectImpl for PcapWriter {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
let mut settings = Settings::default();
|
||||
if let Some(params) = obj.property::<Option<String>>("params") {
|
||||
settings.update_from_params(&obj, params);
|
||||
}
|
||||
|
||||
if settings.target_factory.is_some() || settings.pad_path.is_some() {
|
||||
if let Err(err) = create_dir_all(&settings.output_dir) {
|
||||
gst::error!(
|
||||
CAT,
|
||||
"Could not create output dir, not writing pcaps: {err:?}"
|
||||
)
|
||||
} else {
|
||||
self.register_hook(TracerHook::PadPushPre);
|
||||
self.register_hook(TracerHook::PadPushListPre);
|
||||
}
|
||||
} else {
|
||||
gst::warning!(
|
||||
CAT,
|
||||
obj = obj,
|
||||
"'pcap-writer' enabled without specifying 'target-factory' or 'pad-path' parameters. Not writing pcaps."
|
||||
);
|
||||
}
|
||||
|
||||
*self.settings.borrow_mut() = settings;
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for PcapWriter {}
|
||||
|
||||
fn pad_is_wanted(pad: &gst::Pad, settings: &Settings) -> bool {
|
||||
if let Some(factory_name) = settings.target_factory.as_ref() {
|
||||
return pad.parent().map_or(false, |p| {
|
||||
p.downcast::<gst::Element>().map_or(false, |e| {
|
||||
e.factory().map_or(false, |f| f.name() == *factory_name)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
let mut element_name_pad_name = settings.pad_path.as_ref().unwrap().split(':');
|
||||
let element_name = element_name_pad_name.next().unwrap();
|
||||
let pad_name = element_name_pad_name.next();
|
||||
|
||||
if let Some(parent) = pad.parent() {
|
||||
if parent.name() != element_name {
|
||||
return false;
|
||||
} else if let Some(pad_name) = pad_name {
|
||||
return pad_name == pad.name();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
impl TracerImpl for PcapWriter {
|
||||
fn pad_push_list_pre(&self, _ts: u64, pad: &gst::Pad, blist: &gst::BufferList) {
|
||||
for buffer in blist.iter() {
|
||||
self.maybe_write_buffer(pad, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fn pad_push_pre(&self, _ts: u64, pad: &gst::Pad, buffer: &gst::Buffer) {
|
||||
self.maybe_write_buffer(pad, buffer.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
impl PcapWriter {
|
||||
fn maybe_write_buffer(&self, pad: &gst::Pad, buffer: &gst::BufferRef) {
|
||||
if self
|
||||
.ignored
|
||||
.lock()
|
||||
.unwrap()
|
||||
.contains(&(pad.as_ptr() as usize))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let mut pads = self.pads.lock().unwrap();
|
||||
let writer = if let Some(writer) = pads.get(&(pad.as_ptr() as usize)) {
|
||||
writer.clone()
|
||||
} else if pad_is_wanted(pad, &self.settings.borrow()) {
|
||||
let mut obj = pad.upcast_ref::<gst::Object>().clone();
|
||||
let mut fname = obj.name().to_string();
|
||||
while let Some(p) = obj.parent() {
|
||||
fname = p.name().to_string() + ">" + &fname;
|
||||
obj = p.clone();
|
||||
}
|
||||
|
||||
fname.push_str(".pcap");
|
||||
let outpath = self.settings.borrow().output_dir.join(fname);
|
||||
gst::info!(CAT, obj = pad, "Writing pcap: {outpath:?}");
|
||||
|
||||
let outfile = File::create(outpath).expect("Error creating file");
|
||||
|
||||
let pcap_writer = Arc::new(Mutex::new(Writer::new(
|
||||
outfile,
|
||||
self.settings.borrow().fake_protocol,
|
||||
)));
|
||||
pads.insert(pad.as_ptr() as usize, pcap_writer.clone());
|
||||
|
||||
pcap_writer
|
||||
} else {
|
||||
self.ignored.lock().unwrap().insert(pad.as_ptr() as usize);
|
||||
return;
|
||||
};
|
||||
|
||||
drop(pads);
|
||||
|
||||
let mut writer = writer.lock().unwrap();
|
||||
if let Err(err) = writer.write(buffer) {
|
||||
gst::error!(CAT, "Error writing buffer: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
20
utils/tracers/src/pcap_writer/mod.rs
Normal file
20
utils/tracers/src/pcap_writer/mod.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Copyright (C) 2022 Thibault Saunier <tsaunier@igalia.com>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct PcapWriter(ObjectSubclass<imp::PcapWriter>) @extends gst::Tracer, gst::Object;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Tracer::register(Some(plugin), "pcap-writer", PcapWriter::static_type())
|
||||
}
|
Loading…
Reference in a new issue