From a05ab37b490a0e1a0d9aa79a558a4fcc5275d486 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Sat, 24 Sep 2022 16:47:10 -0300 Subject: [PATCH] tracers: Add a tracer that dumps data flow into .pcap files See documentation for more details Part-of: --- Cargo.lock | 133 ++++++++--- docs/plugins/gst_plugins_cache.json | 1 + utils/tracers/Cargo.toml | 3 + utils/tracers/src/lib.rs | 2 + utils/tracers/src/pcap_writer/imp.rs | 320 +++++++++++++++++++++++++++ utils/tracers/src/pcap_writer/mod.rs | 20 ++ 6 files changed, 445 insertions(+), 34 deletions(-) create mode 100644 utils/tracers/src/pcap_writer/imp.rs create mode 100644 utils/tracers/src/pcap_writer/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a9d16b4a..7a53df53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index f03a6f48..4ea667dd 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -9603,6 +9603,7 @@ "tracers": { "buffer-lateness": {}, "pad-push-timings": {}, + "pcap-writer": {}, "pipeline-snapshot": {}, "queue-levels": {} }, diff --git a/utils/tracers/Cargo.toml b/utils/tracers/Cargo.toml index 00a200f9..b64d1e21 100644 --- a/utils/tracers/Cargo.toml +++ b/utils/tracers/Cargo.toml @@ -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" diff --git a/utils/tracers/src/lib.rs b/utils/tracers/src/lib.rs index dd490f99..6c881bde 100644 --- a/utils/tracers/src/lib.rs +++ b/utils/tracers/src/lib.rs @@ -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(()) } diff --git a/utils/tracers/src/pcap_writer/imp.rs b/utils/tracers/src/pcap_writer/imp.rs new file mode 100644 index 00000000..14cea75d --- /dev/null +++ b/utils/tracers/src/pcap_writer/imp.rs @@ -0,0 +1,320 @@ +// Copyright (C) 2022 Thibault Saunier +// +// 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 +// . +// +// 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 = Lazy::new(|| { + gst::DebugCategory::new( + "pcap-writer", + gst::DebugColorFlags::empty(), + Some("pcap writer tracer"), + ) +}); + +#[derive(Debug)] +struct Settings { + output_dir: PathBuf, + target_factory: Option, + pad_path: Option, + 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, + buf: Vec, + protocol: FakeProtocol, +} + +impl Writer { + fn new(file: File, protocol: FakeProtocol) -> Self { + Self { + writer: pcap::PcapWriter::new(file).expect("Error writing pcap"), + buf: Vec::::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, + + pads: Mutex>>>, + + ignored: Mutex>, +} + +#[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::>("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::().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::().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:?}"); + } + } +} diff --git a/utils/tracers/src/pcap_writer/mod.rs b/utils/tracers/src/pcap_writer/mod.rs new file mode 100644 index 00000000..52b9d42a --- /dev/null +++ b/utils/tracers/src/pcap_writer/mod.rs @@ -0,0 +1,20 @@ +// Copyright (C) 2022 Thibault Saunier +// +// 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 +// . +// +// SPDX-License-Identifier: MPL-2.0 + +use gst::glib; +use gst::prelude::*; + +mod imp; + +glib::wrapper! { + pub struct PcapWriter(ObjectSubclass) @extends gst::Tracer, gst::Object; +} + +pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { + gst::Tracer::register(Some(plugin), "pcap-writer", PcapWriter::static_type()) +}