///|
fn nonce() -> String raise {
let bytes = match @env.rand(16) {
Some(v) => v
None => raise @rtsp.State("secure random unavailable")
}
let out = StringBuilder()
let digits = "0123456789abcdef"
for b in bytes {
let n = b.to_int()
let a = n / 16
let c = n % 16
out.write_string(digits[a:a + 1].to_owned())
out.write_string(digits[c:c + 1].to_owned())
}
out.to_string()
}
///|
async fn main {
let args = @env.args()
if args.length() < 3 {
println(
"usage: moonrtsp inspect|play|publish|publish-multi|follow rtsp://host/path [tcp|udp] [packet-count]",
)
return
}
let mode = args[1]
let uri = args[2]
let udp = args.length() > 3 && args[3] == "udp"
let count = if args.length() > 4 {
@rtsp.parse_count(args[4], 1000000)
} else {
100
}
let credentials = match @env.get_env_var("MOONRTSP_USER") {
Some(user) => {
let password = @env.get_env_var("MOONRTSP_PASSWORD").unwrap_or("")
Some(@rtsp.Credentials::new(user, password))
}
None => None
}
let cnonce = nonce()
if mode == "follow" {
@net.follow(
uri,
[0],
@rtsp.ReconnectPolicy::new(),
client => {
for i = 0; i < count; i = i + 1 {
let event = if udp { client.receive_udp(0) } else { client.receive() }
if event is Rtp(track, packet) {
println(
"rtp track=" +
track.to_string() +
" seq=" +
packet.sequence_number().to_string(),
)
}
}
},
udp~,
credentials?,
cnonce~,
)
return
}
let client = @net.Client::connect(uri, credentials?, cnonce~)
defer client.close()
if mode == "publish" || mode == "publish-multi" {
let mut sdp = "v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\ns=MoonRTSP synthetic G711\r\nt=0 0\r\na=control:*\r\nm=audio 0 RTP/AVP 0\r\na=rtpmap:0 PCMU/8000\r\na=control:trackID=0\r\n"
if mode == "publish-multi" {
sdp += "m=audio 0 RTP/AVP 8\r\na=rtpmap:8 PCMA/8000\r\na=control:trackID=1\r\n"
}
ignore(client.announce(sdp))
ignore(client.setup(0, udp~, record=true))
if mode == "publish-multi" {
ignore(client.setup(1, udp~, record=true))
}
client.record()
println("recording")
for i = 0; i < count; i = i + 1 {
let packet = @rtp.Packet::new(
payload_type=b'\x00',
sequence_number=(i % 65536).to_uint16(),
timestamp=i.reinterpret_as_uint() * 160U,
ssrc=123456U,
marker=i == 0,
payload=Bytes::make(160, b'\xff'),
)
client.send_packet(0, packet)
if mode == "publish-multi" {
let other = @rtp.Packet::new(
payload_type=b'\x08',
sequence_number=(i % 65536).to_uint16(),
timestamp=i.reinterpret_as_uint() * 160U,
ssrc=654321U,
marker=i == 0,
payload=Bytes::make(160, b'\xd5'),
)
client.send_packet(1, other)
}
@async.sleep(20)
}
client.teardown()
println("published packets=" + count.to_string())
} else if mode == "inspect" || mode == "play" {
let methods = client.options()
println("methods=" + methods.join(","))
let description = client.describe()
for track in description.tracks {
println(
"track=" +
track.index.to_string() +
" kind=" +
track.kind +
" formats=" +
track.formats.join(","),
)
}
if mode == "inspect" {
return
}
for track in description.tracks {
ignore(client.setup(track.index, udp~))
}
ignore(client.play())
let mut received = 0
while received < count {
let event = if udp {
client.receive_udp(description.tracks[0].index)
} else {
client.receive()
}
if event is Rtp(track, packet) {
received += 1
println(
"rtp track=" +
track.to_string() +
" seq=" +
packet.sequence_number().to_string() +
" bytes=" +
packet.payload().length().to_string(),
)
}
if client.keepalive_due() {
client.keepalive()
}
}
client.pause()
ignore(client.play())
client.keepalive()
client.teardown()
println("received packets=" + received.to_string())
} else {
raise @rtsp.Invalid("unknown command")
}
}