SharpCaster is a cross-platform C# SDK for communicating with Google Chromecast devices. It enables .NET applications to discover, connect, launch apps, and control media playback on Chromecast devices with support for both .NET Standard 2.0 and .NET 9 (including AOT compilation).
- Features
- Installation
- Quick Start
- Comprehensive Examples
- Media Queue Management
- Volume Control
- Event Handling
- Custom Chromecast Channels
- Troubleshooting
- Contributing
- License
âś… Device Discovery: Automatic discovery of Chromecast devices on your local network using mDNS
âś… Media Control: Complete media playback control (play, pause, stop, seek, volume)
âś… Queue Management: Support for media queues with navigation (next, previous, shuffle, repeat)
âś… Application Management: Launch and manage Chromecast applications
âś… Custom Channels: Extensible architecture for custom Chromecast channels
âś… Cross-Platform: Compatible with .NET Standard 2.0 and .NET 9
âś… AOT Ready: Full support for Native AOT compilation in .NET 9
âś… Async/Await: Modern async programming model throughout
âś… Event-Driven: Rich event system for real-time status updates
Install via NuGet Package Manager:
Install-Package SharpCaster
Or via .NET CLI:
dotnet add package SharpCaster
using Sharpcaster;
using Sharpcaster.Models;
using Sharpcaster.Models.Media;
// Discover Chromecast devices
var locator = new MdnsChromecastLocator();
var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token;
var chromecasts = await locator.FindReceiversAsync(cancellationToken);
if (!chromecasts.Any())
{
Console.WriteLine("No Chromecast devices found");
return;
}
// Connect to first found device
var chromecast = chromecasts.First();
var client = new ChromecastClient();
await client.ConnectChromecast(chromecast);
Console.WriteLine($"Connected to {chromecast.Name}");
// Launch the default media receiver app
await client.LaunchApplicationAsync("CC1AD845"); // Default Media Receiver
// Create and load media
var media = new Media
{
ContentUrl = "https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/DesigningForGoogleCast.mp4",
ContentType = "video/mp4",
Metadata = new MediaMetadata
{
Title = "Sample Video",
SubTitle = "A demonstration video"
}
};
var mediaStatus = await client.MediaChannel.LoadAsync(media);
Console.WriteLine($"Media loaded: {mediaStatus.PlayerState}");
public class ChromecastMediaPlayer
{
private ChromecastClient _client;
private ChromecastReceiver _device;
public async Task<bool> ConnectAsync(string deviceName = null)
{
try
{
var locator = new MdnsChromecastLocator();
var devices = await locator.FindReceiversAsync(CancellationToken.None);
_device = deviceName != null
? devices.FirstOrDefault(d => d.Name.Contains(deviceName))
: devices.FirstOrDefault();
if (_device == null) return false;
_client = new ChromecastClient();
await _client.ConnectChromecast(_device);
// Subscribe to events
_client.MediaChannel.StatusChanged += OnMediaStatusChanged;
_client.Disconnected += OnDisconnected;
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
return false;
}
}
public async Task PlayVideoAsync(string url, string title = null)
{
await _client.LaunchApplicationAsync("CC1AD845"); // Default Media Receiver
var media = new Media
{
ContentUrl = url,
ContentType = GetContentType(url),
Metadata = new MediaMetadata
{
Title = title ?? Path.GetFileNameWithoutExtension(url),
MetadataType = MetadataType.Movie
}
};
await _client.MediaChannel.LoadAsync(media);
}
public async Task PlayAudioAsync(string url, string title = null, string artist = null)
{
await _client.LaunchApplicationAsync("CC1AD845");
var media = new Media
{
ContentUrl = url,
ContentType = GetContentType(url),
Metadata = new MusicTrackMetadata
{
Title = title ?? Path.GetFileNameWithoutExtension(url),
Artist = artist,
MetadataType = MetadataType.MusicTrack
}
};
await _client.MediaChannel.LoadAsync(media);
}
private void OnMediaStatusChanged(object sender, MediaStatus status)
{
Console.WriteLine($"Media Status: {status.PlayerState} - {status.CurrentTime:F1}s");
}
private void OnDisconnected(object sender, EventArgs e)
{
Console.WriteLine("Disconnected from Chromecast");
}
private static string GetContentType(string url)
{
var extension = Path.GetExtension(url).ToLower();
return extension switch
{
".mp4" => "video/mp4",
".mp3" => "audio/mpeg",
".wav" => "audio/wav",
".webm" => "video/webm",
_ => "video/mp4"
};
}
}
SharpCaster supports advanced queue operations for playlist-style media playback:
// Create a media queue
var queueItems = new[]
{
new QueueItem
{
Media = new Media
{
ContentUrl = "https://example.com/song1.mp3",
ContentType = "audio/mpeg",
Metadata = new MusicTrackMetadata { Title = "Song 1", Artist = "Artist 1" }
}
},
new QueueItem
{
Media = new Media
{
ContentUrl = "https://example.com/song2.mp3",
ContentType = "audio/mpeg",
Metadata = new MusicTrackMetadata { Title = "Song 2", Artist = "Artist 2" }
}
}
};
// Load queue with repeat mode
await client.MediaChannel.QueueLoadAsync(queueItems, 0, RepeatModeType.ALL);
// Navigate through queue
await client.MediaChannel.QueueNextAsync(); // Next track
await client.MediaChannel.QueuePrevAsync(); // Previous track
// Get queue information
var itemIds = await client.MediaChannel.QueueGetItemIdsAsync();
var items = await client.MediaChannel.QueueGetItemsAsync(itemIds);
// Get current volume
var status = await client.ReceiverChannel.GetChromecastStatusAsync();
Console.WriteLine($"Current volume: {status.Volume.Level:P0}");
// Set volume (0.0 to 1.0)
await client.ReceiverChannel.SetVolumeAsync(0.5f);
// Mute/unmute
await client.ReceiverChannel.SetMutedAsync(true);
await client.ReceiverChannel.SetMutedAsync(false);
SharpCaster provides rich event support for real-time updates:
// Media events
client.MediaChannel.StatusChanged += (sender, status) =>
{
Console.WriteLine($"Player State: {status.PlayerState}");
Console.WriteLine($"Current Time: {status.CurrentTime}s");
Console.WriteLine($"Duration: {status.Media?.Duration}s");
};
// Connection events
client.Disconnected += (sender, args) =>
{
Console.WriteLine("Connection lost to Chromecast");
};
// Application events
client.ReceiverChannel.StatusChanged += (sender, status) =>
{
foreach (var app in status.Applications ?? [])
{
Console.WriteLine($"Running app: {app.DisplayName} ({app.AppId})");
}
};
Create custom channels for specialized applications:
public class CustomGameChannel : ChromecastChannel
{
public CustomGameChannel(ILogger<CustomGameChannel> logger)
: base("custom.game", logger) { }
public async Task SendGameCommandAsync(string command, object data)
{
var message = new { type = command, data = data };
await SendAsync(JsonSerializer.Serialize(message));
}
protected override async Task OnMessageReceivedAsync(string message, string messageType)
{
// Handle custom game messages
var gameMessage = JsonSerializer.Deserialize<GameMessage>(message);
// Process game-specific logic
}
}
// Register and use custom channel
var gameChannel = new CustomGameChannel(logger);
client.RegisterChannel(gameChannel);
You can also reverse engineer existing channels:
- In Chrome, go to
chrome://net-export/
- Select 'Include raw bytes (will include cookies and credentials)'
- Click 'Start Logging to Disk'
- Cast from your favorite web app
- Stop logging and open the log in netlog-viewer
- Search for
type:SOCKET
and find familiar JSON data - Collect the exchanged JSON
- Create a new class inheriting from
ChromecastChannel
and implement your logic
Device Discovery Issues:
// Increase discovery timeout for slow networks
var cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
var devices = await locator.FindReceiversAsync(cancellationToken);
// Manually specify device if discovery fails
var manualDevice = new ChromecastReceiver
{
Name = "My Chromecast",
DeviceUri = new Uri("http://192.168.1.100"),
Port = 8009
};
Connection Timeouts:
// Enable logging for debugging
var loggerFactory = LoggerFactory.Create(builder =>
builder.AddConsole().SetMinimumLevel(LogLevel.Debug));
var client = new ChromecastClient(loggerFactory);
Media Loading Failures:
- Ensure media URLs are publicly accessible
- Verify correct
ContentType
is specified - Check that the Chromecast supports the media format
- Use HTTPS URLs when possible
Network Issues:
- Ensure device is on the same network as Chromecast
- Check firewall settings (port 8009 must be accessible)
- Verify mDNS/Bonjour is enabled on the network
try
{
await client.MediaChannel.LoadAsync(media);
}
catch (TimeoutException)
{
Console.WriteLine("Request timed out - check network connection");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Invalid operation: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected error: {ex.Message}");
}
- .NET Standard 2.0 - Maximum compatibility across .NET implementations
- .NET 9 - Latest features including Native AOT support
- Compatible with: .NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+, Xamarin, Unity
We welcome contributions! Here's how you can help:
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
git clone https://github.com/Tapanila/SharpCaster.git
cd SharpCaster
dotnet restore
dotnet build
dotnet test
Tests require a physical Chromecast device on the network:
dotnet test
Some of the tests may require few tries to pass due to network conditions.
This project is licensed under the MIT License - see the LICENSE file for details.