Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

main #24

Closed
wants to merge 9 commits into from
Closed

main #24

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 191 additions & 55 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ byteorder = "1.4.3"
log = "0.4.17"
openssl = "0.10.45"
protobuf = "=3.2.0"
serde = "1.0.152"
serde_derive = "1.0.152"
serde_json = "1.0.91"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.93"

[dev-dependencies]
ansi_term = "0.12.1"
docopt = "1.1.1"
env_logger = "0.10.0"
mdns-sd = "0.6.1"

[build-dependencies]
protobuf-codegen = "=3.2.0"

[features]
thread_safe = []
thread_safe = []
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
![Build Status](https://github.com/azasypkin/rust-cast/actions/workflows/ci.yml/badge.svg)

# Usage

* [Documentation](https://docs.rs/crate/rust_cast/)
* Try out [Rust Caster](./examples/rust_caster.rs) example to see this crate in action!
* Try out [Rust Caster](examples/rust_caster.rs) example to see this crate in action!

# Build

Proto files are taken from [Chromium Open Screen GitHub mirror](https://chromium.googlesource.com/openscreen/+/8cce349b0a595ddf7178d5730e980ace3a1d1a53/cast/common/channel/proto).
Proto files are taken
from [Chromium Open Screen GitHub mirror](https://chromium.googlesource.com/openscreen/+/8cce349b0a595ddf7178d5730e980ace3a1d1a53/cast/common/channel/proto).

By default `cargo build` won't try to generate Rust code from the files located at `protobuf/*`, if you want to do that
use `GENERATE_PROTO` environment variable during build and make sure you have `protoc` binary in `$PATH`:
Expand All @@ -20,7 +22,9 @@ $ GENERATE_PROTO=true cargo build

## Generic features

First, you need to figure out the address of the device to connect to. For example, you can use `avahi` with the following command:
First, you need to figure out the address of the device to connect to. For example, you can use `avahi` with the
following command:

```bash
$ avahi-browse -a --resolve
```
Expand All @@ -47,9 +51,10 @@ The following app has been stopped: Default Media Receiver (CC1AD845)
```

## Media features

```bash
// Stream a video.
$ cargo run --example rust_caster -- -a 192.168.0.100 -m http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
$ cargo run --example rust_caster -- -a 192.168.0.100 -m https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4

// Stream a video of specific format with buffering.
$ cargo run --example rust_caster -- -a 192.168.0.100 -m http://xxx.webm --media-type video/webm --media-stream-type buffered
Expand All @@ -76,15 +81,16 @@ $ cargo run --example rust_caster -- -a 192.168.0.100 --media-app youtube --medi
$ cargo run --example rust_caster -- -a 192.168.0.100 --media-app youtube --media-seek 100
```

For all possible values of `--media-type` see [Supported Media for Google Cast](https://developers.google.com/cast/docs/media).
For all possible values of `--media-type`
see [Supported Media for Google Cast](https://developers.google.com/cast/docs/media).

# DNS TXT Record description

* `md` - Model Name (e.g. "Chromecast");
* `id` - UUID without hyphens of the particular device (e.g. xx12x3x456xx789xx01xx234x56789x0);
* `fn` - Friendly Name of the device (e.g. "Living Room");
* `fn` - Friendly Name of the device (e.g. "Living Room");
* `rs` - Unknown (recent share???) (e.g. "Youtube TV");
* `bs` - Uknonwn (e.g. "XX1XXX2X3456");
* `bs` - Unknown (e.g. "XX1XXX2X3456");
* `st` - Unknown (e.g. "1");
* `ca` - Unknown (e.g. "1234");
* `ic` - Icon path (e.g. "/setup/icon.png");
Expand Down
74 changes: 55 additions & 19 deletions examples/rust_caster.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
extern crate ansi_term;
extern crate docopt;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rust_cast;
extern crate serde;
#[macro_use]
extern crate serde_derive;

use std::str::FromStr;

use ansi_term::Colour::{Green, Red};

use docopt::Docopt;
use log::error;
use mdns_sd::{ServiceDaemon, ServiceEvent};
use serde::Deserialize;

use rust_cast::{
channels::{
Expand All @@ -23,6 +15,7 @@ use rust_cast::{
CastDevice, ChannelMessage,
};

const SERVICE_TYPE: &str = "_googlecast._tcp.local.";
const DEFAULT_DESTINATION_ID: &str = "receiver-0";

const USAGE: &str = "
Expand Down Expand Up @@ -264,22 +257,65 @@ fn play_media(
}
}

fn discover() -> Option<(String, u16)> {
let mdns = ServiceDaemon::new().expect("Failed to create mDNS daemon.");

let receiver = mdns
.browse(SERVICE_TYPE)
.expect("Failed to browse mDNS services.");

while let Ok(event) = receiver.recv() {
match event {
ServiceEvent::ServiceResolved(info) => {
let mut addresses = info
.get_addresses()
.iter()
.map(|address| address.to_string())
.collect::<Vec<_>>();
println!(
"{}{}",
Green.paint("Resolved a new service: "),
Red.paint(format!(
"{} ({})",
info.get_fullname(),
addresses.join(", ")
))
);

// Based on mDNS crate code we should have at least one address available.
return Some((addresses.remove(0), info.get_port()));
}
other_event => {
println!(
"{}{}",
Green.paint("Received other service event: "),
Red.paint(format!("{:?}", other_event))
);
}
}
}
None
}

fn main() {
env_logger::init();

let args: Args = Docopt::new(USAGE)
.and_then(|d| d.deserialize())
.unwrap_or_else(|e| e.exit());

if args.flag_address.is_none() {
println!("Please specify Cast Device address!");
std::process::exit(1);
}
let (address, port) = match args.flag_address {
Some(address) => (address, args.flag_port),
None => {
println!("Cast Device address is not specified, trying to discover...");
discover().unwrap_or_else(|| {
println!("No Cast device discovered, please specify device address explicitly.");
std::process::exit(1);
})
}
};

let cast_device = match CastDevice::connect_without_host_verification(
args.flag_address.unwrap(),
args.flag_port,
) {
let cast_device = match CastDevice::connect_without_host_verification(address, port) {
Ok(cast_device) => cast_device,
Err(err) => panic!("Could not establish connection with Cast Device: {:?}", err),
};
Expand Down
3 changes: 1 addition & 2 deletions protobuf/authority_keys.proto
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

Expand All @@ -10,7 +10,6 @@ option optimize_for = LITE_RUNTIME;
// current namespacing of the library. Also, this file should probably be moved
// to the public directory. And, all of this will have to be coordinated with a
// DEPS roll in Chromium (since Chromium code depends on this).

package cast.channel;

message AuthorityKeys {
Expand Down
7 changes: 5 additions & 2 deletions protobuf/cast_channel.proto
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright 2019 The Chromium Authors. All rights reserved.
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

syntax = "proto2";

option optimize_for = LITE_RUNTIME;

// TODO(crbug.com/openscreen/90): Rename to openscreen.cast, to update to the
// current namespacing of the library. Also, this file should probably be moved
// to the public directory. And, all of this will have to be coordinated with a
// DEPS roll in Chromium (since Chromium code depends on this).

package cast.channel;

message CastMessage {
Expand Down Expand Up @@ -58,11 +58,14 @@ message CastMessage {
// will always be set.
optional string payload_utf8 = 6;
optional bytes payload_binary = 7;

// --- Begin new 1.1 fields.

// Flag indicating whether there are more chunks to follow for this message.
// If the flag is false or is not present, then this is the last (or only)
// chunk of the message.
optional bool continued = 8;

// If this is a chunk of a larger message, and the remaining length of the
// message payload (the sum of the lengths of the payloads of the remaining
// chunks) is known, this field will indicate that length. For a given
Expand Down
32 changes: 16 additions & 16 deletions src/cast/authority_keys.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file is generated by rust-protobuf 3.2.0. Do not edit
// .proto file is parsed by protoc 3.21.12
// This file is generated by rust-protobuf 3.3.0. Do not edit
// .proto file is parsed by protoc 24.4
// @generated

// https://github.com/rust-lang/rust-clippy/issues/702
Expand All @@ -24,10 +24,10 @@

/// Generated files are compatible only with the same version
/// of protobuf runtime.
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_3_0;

#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:cast.channel.AuthorityKeys)
#[derive(PartialEq, Clone, Default, Debug)]
pub struct AuthorityKeys {
// message fields
// @@protoc_insertion_point(field:cast.channel.AuthorityKeys.keys)
Expand Down Expand Up @@ -124,8 +124,8 @@ impl ::protobuf::Message for AuthorityKeys {

/// Nested message and enums of message `AuthorityKeys`
pub mod authority_keys {
#[derive(PartialEq,Clone,Default,Debug)]
// @@protoc_insertion_point(message:cast.channel.AuthorityKeys.Key)
#[derive(PartialEq, Clone, Default, Debug)]
pub struct Key {
// message fields
// @@protoc_insertion_point(field:cast.channel.AuthorityKeys.Key.fingerprint)
Expand Down Expand Up @@ -251,6 +251,17 @@ pub mod authority_keys {
::std::result::Result::Ok(())
}

fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
if let Some(v) = self.fingerprint.as_ref() {
os.write_bytes(1, v)?;
}
if let Some(v) = self.public_key.as_ref() {
os.write_bytes(2, v)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}

// Compute sizes of nested messages
#[allow(unused_variables)]
fn compute_size(&self) -> u64 {
Expand All @@ -266,17 +277,6 @@ pub mod authority_keys {
my_size
}

fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
if let Some(v) = self.fingerprint.as_ref() {
os.write_bytes(1, v)?;
}
if let Some(v) = self.public_key.as_ref() {
os.write_bytes(2, v)?;
}
os.write_unknown_fields(self.special_fields.unknown_fields())?;
::std::result::Result::Ok(())
}

fn special_fields(&self) -> &::protobuf::SpecialFields {
&self.special_fields
}
Expand Down
Loading
Loading