Skip to content

Commit

Permalink
Merge pull request #70 from gomadoufu/feat-#69
Browse files Browse the repository at this point in the history
  • Loading branch information
gomadoufu authored Feb 20, 2023
2 parents 69c1ba9 + 0b0c15e commit 1d06df6
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 172 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "gomast"
version = "0.3.0"
version = "0.4.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
58 changes: 18 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,27 @@
Thin wrapper for GStreamer, for development and testing on RaspberryPi.

```sh
# use default options (test video, vga, 30fps, h264)
$ gomast example.com 5000
Running: gst-launch-1.0 videotestsrc ! video/x-raw,width=640,height=480,framerate=30/1 ! videoconvert ! x264enc ! rtph264pay ! udpsink host=example.com port=5000
# input: testpattern, YUY2
# output: 640x480, vp8, example.com:5004
$ gomast input test yuy2 output 640 480 vp8 example.com 5004
Running: gst-launch-1.0 videotestsrc ! video/x-raw,format=YUY2,width=640,height=480 ! videoconvert ! vp8enc ! rtpvp8pay ! udpsink host=example.com port=5004
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
...

# use custom options (mipi video, hd, 60fps, vp8)
$ gomast myserver.com 7000 -i mipi -f 60 -t vp8 -r hd
Running: gst-launch-1.0 libcamerasrc ! video/x-raw,width=1280,height=720,framerate=60/1 ! videoconvert ! vp8enc ! rtpvp8pay ! udpsink host=myserver.com port=7000
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
...

# use hardware encoder
$ gomast myserver.com 7000 -9 usb --hardware
Running: gst-launch-1.0 -v v4l2src ! video/x-raw,width=640,height=480,framerate=30/1 ! videoconvert ! v4l2h264enc ! 'video/x-h264,level=(string)4' ! rtph264pay ! udpsink host=myserver.com port=7000
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
...

Redistribute latency...
Pipeline is PREROLLED ...
Setting pipeline to PLAYING ...
Redistribute latency...
New clock: GstSystemClock
0:00:06.3 / 99:99:99.

# input: usb camera, YUY2
# output: 1280x720, h264, example.com:7001, hardware encode
# dry-run mode
$ gomast myserver.com 7000 --dry-run
gst-launch-1.0 videotestsrc ! video/x-raw,width=640,height=480,framerate=30/1 ! videoconvert ! x264enc ! rtph264pay ! udpsink host=myserver.com port=7000
$ gomast input usb yuy2 output 1280 720 h264 example.com 7001 --hardware --dry-run
gst-launch-1.0 -v v4l2src ! video/x-raw,format=YUY2,width=1280,height=720 ! videoconvert ! v4l2h264enc ! 'video/x-h264,level=(string)4' ! rtph264pay ! udpsink host=example.com port=7001

# show available devices
$ gomast --show | grep h264
$ gomast show | grep h264
applemedia: vtenc_h264: H.264 encoder
applemedia: vtenc_h264_hw: H.264 (HW only) encoder
codectimestamper: h264timestamper: H.264 timestamper
Expand Down Expand Up @@ -58,28 +52,12 @@ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreame

## Download

RaspberryPiOS(armv7-unknown-linux-musl), Linux(unknown-linux-musl), and MacOS(apple-darwin)
RaspberryPiOS(armv7-unknown-linux-musl), Linux(unknown-linux-musl), and MacOS(apple-darwin)
binaries are available from [Release](http://github.com/gomadoufu/gomastreamer/releases) page.

## Usage

Format: `gomast [OPTIONS] [HOST] [PORT]`.

You can specify the following options:

-i, --input : Source of video [default: test] [possible values: test, mipi, usb]

-r, --resolution : Resolution of video [default: vga] [possible values: vga, sd, hd]

-f, --framerate : Framerate of video [default: 30]

-t, --type : Format of video [default: h264] [possible values: vp8, h264]

--hardware : Use hardware encode

--dry-run : Dry-run mode, just print command

--show : Show information of devices
`gomast input <INPUT_TYPE> <FORMAT> output <WIDTH> <HEIGHT> <ENCODER> <HOST> <PORT> [options]`

The HOST and PORT arguments correspond to the host and port arguments of the GStreamer `udpsink` element. There are required arguments.

Expand Down
89 changes: 89 additions & 0 deletions src/executor/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::parser::{
destruct::Elements,
parse::{InputType, OutputFormat},
};

use std::{collections::HashMap, vec};

#[derive(Debug)]
pub struct Builder {
arguments: Elements,
arg_map: HashMap<String, String>,
}

impl Builder {
pub fn new(arguments: Elements) -> Builder {
let arg_map = map_args()
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
Builder { arguments, arg_map }
}
pub fn build(&self) -> Vec<String> {
let mut result = vec![];
result.push(self.match_input_type());
result.push("!".to_string());
// カメラから、指定された解像度の映像を取得しておく。後で変換しない。
result.push(format!(
"{},{},{}",
self.arguments.iformat,
format_args!("width={}", self.arguments.iwidth),
format_args!("height={}", self.arguments.iheight),
));
result.push("!".to_string());
result.push("videoconvert".to_string());
result.push("!".to_string());
result.push(self.match_oformat());
result.push("!".to_string());
result.push(self.match_rtp());
result.push("!".to_string());
result.push("udpsink".to_string());
result.push(format!("host={}", self.arguments.ohost));
result.push(format!("port={}", self.arguments.oport));

result
}

fn match_input_type(&self) -> String {
match self.arguments.itype {
InputType::Test => self.arg_map.get("test").unwrap().to_string(),
InputType::Mipi => self.arg_map.get("mipi").unwrap().to_string(),
InputType::Usb => self.arg_map.get("usb").unwrap().to_string(),
}
}

fn match_oformat(&self) -> String {
match &self.arguments.oformat {
OutputFormat::H264 if self.arguments.hardware => {
self.arg_map.get("h264hard").unwrap().to_string()
}
OutputFormat::H264 => self.arg_map.get("h264soft").unwrap().to_string(),
OutputFormat::Vp8 if self.arguments.hardware => {
self.arg_map.get("vp8hard").unwrap().to_string()
}
OutputFormat::Vp8 => self.arg_map.get("vp8soft").unwrap().to_string(),
}
}

fn match_rtp(&self) -> String {
match self.arguments.oformat {
OutputFormat::H264 => self.arg_map.get("h264rtp").unwrap().to_string(),
OutputFormat::Vp8 => self.arg_map.get("vp8rtp").unwrap().to_string(),
}
}
}

fn map_args() -> Vec<(&'static str, &'static str)> {
let result = vec![
("test", "videotestsrc"),
("mipi", "libcamerasrc"),
("usb", "-v v4l2src"),
("h264soft", "x264enc"),
("h264hard", "v4l2h264enc ! 'video/x-h264,level=(string)4'"),
("vp8soft", "vp8enc"),
("vp8hard", "omxvp8enc"),
("vp8rtp", "rtpvp8pay"),
("h264rtp", "rtph264pay"),
];
result
}
1 change: 1 addition & 0 deletions src/executor/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod build;
pub mod exec;
19 changes: 11 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
mod executor;
mod parser;
use crate::executor::exec::Exec;
use crate::parser::{convert::ArgConverter, parse::Cli};
use crate::{
executor::{build::Builder, exec::Exec},
parser::{destruct::Elements, parse::Cli},
};
use clap::Parser;

fn main() {
let cli = Cli::parse();
let is_dry_run = cli.dry_run;
if cli.show {
let destructed = Elements::destruct(cli);
let dry_run = destructed.dry_run;
if destructed.show {
let gst_plugins = Exec::new("gst-inspect-1.0".to_string(), vec![]);
let v4l2_ctl = Exec::new("v4l2-ctl --list-devices".to_string(), vec![]);
if is_dry_run {
if dry_run {
println!("gst-inspect-1.0 \n v4l2_ctl --list-devices");
return;
}
Expand All @@ -20,9 +23,9 @@ fn main() {
gst_plugins.exec();
return;
}
let converter = ArgConverter::new();
let result = converter.convert(cli);
if is_dry_run {
let builder = Builder::new(destructed);
let result = builder.build();
if dry_run {
println!("gst-launch-1.0 {}", result.join(" "));
return;
}
Expand Down
97 changes: 0 additions & 97 deletions src/parser/convert.rs

This file was deleted.

62 changes: 62 additions & 0 deletions src/parser/destruct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use super::parse::{Cli, Command, InputFormat, InputType, OutputCommands, OutputFormat};

#[derive(Debug)]
pub struct Elements {
pub itype: InputType,
pub iformat: InputFormat,
pub iwidth: i32,
pub iheight: i32,
pub oformat: OutputFormat,
pub ohost: String,
pub oport: i32,
pub hardware: bool,
pub dry_run: bool,
pub show: bool,
}

impl Elements {
pub fn destruct(cli: Cli) -> Elements {
match cli.input {
Command::Show => Elements {
itype: InputType::Test,
iformat: InputFormat::Yuy2,
iwidth: 0,
iheight: 0,
oformat: OutputFormat::H264,
ohost: "".to_string(),
oport: 0,
hardware: false,
dry_run: false,
show: true,
},
Command::Input(input) => {
let itype = input.input_type;
let iformat = input.format;
match input.output {
OutputCommands::Output(output) => {
let iwidth = output.width;
let iheight = output.height;
let oformat = output.format;
let ohost = output.host;
let oport = output.port;
let hardware = output.hardware_encode;
let dry_run = output.dry_run;
let show = false;
Elements {
itype,
iformat,
iwidth,
iheight,
oformat,
ohost,
oport,
hardware,
dry_run,
show,
}
}
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod convert;
pub mod destruct;
pub mod parse;
Loading

0 comments on commit 1d06df6

Please sign in to comment.