diff --git a/Aurora/Design/Views/Party/PartyViewModel.cs b/Aurora/Design/Views/Party/PartyViewModel.cs
index d6359a8..a7db796 100644
--- a/Aurora/Design/Views/Party/PartyViewModel.cs
+++ b/Aurora/Design/Views/Party/PartyViewModel.cs
@@ -225,7 +225,7 @@ namespace Aurora.Design.Views.Party
{
SetState(PartyState.Connecting);
_client.Start(Hostname, SettingsService.Instance.DefaultPort.ToString());
- await JoinParty();
+ await JoinParty(false);
//TODO add cancellation token
try
@@ -252,7 +252,7 @@ namespace Aurora.Design.Views.Party
string localHost = ServerService.GetLocalIPAddress();
_client.IsHost = true;
_client.Start(localHost, SettingsService.Instance.DefaultPort.ToString());
- await JoinParty();
+ await JoinParty(true);
//TODO add cancellation token
@@ -341,7 +341,7 @@ namespace Aurora.Design.Views.Party
/// Join the remote party.
///
///
- private async Task JoinParty()
+ private async Task JoinParty(bool asHost)
{
try
{
@@ -383,6 +383,7 @@ namespace Aurora.Design.Views.Party
meta.Duration = data.Duration;
RemoteAudio remote = new RemoteAudio(data.Id,
+ asHost,
meta,
_client.RemotePlaybackClient,
_client.RemoteSyncClient);
diff --git a/Aurora/Models/Media/BaseMedia.cs b/Aurora/Models/Media/BaseMedia.cs
index 0e31597..c3a22e8 100644
--- a/Aurora/Models/Media/BaseMedia.cs
+++ b/Aurora/Models/Media/BaseMedia.cs
@@ -6,35 +6,39 @@ namespace Aurora.Models.Media
{
public abstract class BaseMedia
{
- private bool _loaded;
private Stream _stream;
public BaseMedia()
{
- _loaded = false;
Id = Guid.NewGuid().ToString();
}
#region Properties
public string Id { get; protected set; }
+ public abstract MediaTypeEnum MediaType { get; }
+
+ public abstract BaseMetadata Metadata { get; protected set; }
+
+ public bool IsLoaded
+ {
+ get
+ {
+ return DataStream != null;
+ }
+ }
+
#endregion Properties
public virtual Task Load()
{
- _loaded = true;
return Task.FromResult(default(object));
}
public virtual void Unload()
{
- _loaded = false;
}
- public abstract MediaTypeEnum MediaType { get; }
-
- public abstract BaseMetadata Metadata { get; protected set; }
-
///
/// Gets or sets the data stream that holds the song.
diff --git a/Aurora/Models/Media/LocalAudio.cs b/Aurora/Models/Media/LocalAudio.cs
index 6b6ec7b..3d7bd0f 100644
--- a/Aurora/Models/Media/LocalAudio.cs
+++ b/Aurora/Models/Media/LocalAudio.cs
@@ -12,6 +12,12 @@ namespace Aurora.Models.Media
LoadMetadata();
}
+ public LocalAudio(LocalAudio copy)
+ {
+ File = copy.File;
+ LoadMetadata();
+ }
+
#region Properties
public FileInfo File { get; private set; }
diff --git a/Aurora/Models/Media/RemoteAudio.cs b/Aurora/Models/Media/RemoteAudio.cs
index 6b2ce4e..679b57e 100644
--- a/Aurora/Models/Media/RemoteAudio.cs
+++ b/Aurora/Models/Media/RemoteAudio.cs
@@ -15,7 +15,9 @@ namespace Aurora.Models.Media
private CancellationTokenSource _cancellationTokenSource;
#region Constructor
- public RemoteAudio(string id, AudioMetadata metadata,
+ public RemoteAudio(string id,
+ bool fromHost,
+ AudioMetadata metadata,
RemotePlaybackService.RemotePlaybackServiceClient playbackClient,
RemoteSyncService.RemoteSyncServiceClient syncClient)
{
@@ -23,6 +25,7 @@ namespace Aurora.Models.Media
this._remotePlaybackClient = playbackClient;
this._remoteSyncClient = syncClient;
this.Metadata = metadata;
+ this.FromHost = fromHost;
_cancellationTokenSource = new CancellationTokenSource();
}
@@ -46,6 +49,7 @@ namespace Aurora.Models.Media
}
}
+ public bool FromHost { get; private set; }
#endregion Properties
///
@@ -56,15 +60,21 @@ namespace Aurora.Models.Media
this.DataStream = new MemoryStream();
using (var call = _remotePlaybackClient.GetSongStream(new SongRequest() { Id = this.Id }))
{
- while (await call.ResponseStream.MoveNext(_cancellationTokenSource.Token))
+ try
{
- Chunk chunk = call.ResponseStream.Current;
- byte[] buffer = chunk.Content.ToByteArray();
+ while (await call.ResponseStream.MoveNext(_cancellationTokenSource.Token))
+ {
+ Chunk chunk = call.ResponseStream.Current;
+ byte[] buffer = chunk.Content.ToByteArray();
- await this.DataStream.WriteAsync(buffer, 0, buffer.Length);
+ await this.DataStream.WriteAsync(buffer, 0, buffer.Length);
+ }
+ Console.WriteLine("Done receiving stream");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Exception caught while loading remote audio:" + ex.Message);
}
- Console.WriteLine("Done receiving stream");
-
}
await base.Load();
}
diff --git a/Aurora/RemoteImpl/RemotePlaybackImpl.cs b/Aurora/RemoteImpl/RemotePlaybackImpl.cs
index 63d0fa0..17da449 100644
--- a/Aurora/RemoteImpl/RemotePlaybackImpl.cs
+++ b/Aurora/RemoteImpl/RemotePlaybackImpl.cs
@@ -15,19 +15,38 @@ namespace Aurora.RemoteImpl
Grpc.Core.IServerStreamWriter responseStream,
Grpc.Core.ServerCallContext context)
{
- BaseMedia song = LibraryService.Instance.GetSong(request.Id);
- await song.Load();
-
- //Send stream
- Console.WriteLine("Begin sending file");
- byte[] buffer = new byte[2048]; // read in chunks of 2KB
- int bytesRead;
- while ((bytesRead = song.DataStream.Read(buffer, 0, buffer.Length)) > 0)
+ BaseMedia originalSong = LibraryService.Instance.GetSong(request.Id);
+ if (!(originalSong is LocalAudio))
{
- Google.Protobuf.ByteString bufferByteString = Google.Protobuf.ByteString.CopyFrom(buffer);
- await responseStream.WriteAsync(new Chunk { Content = bufferByteString });
+ return;
+ }
+
+ //Copy media object to not interfere with other threads
+ LocalAudio songCopy = new LocalAudio((LocalAudio)originalSong);
+
+ try
+ {
+ //Load only if not already loaded. (Multiple clients may be requesting media)
+ if (!songCopy.IsLoaded)
+ {
+ await songCopy.Load();
+ }
+
+ //Send stream
+ Console.WriteLine("Begin sending file");
+ byte[] buffer = new byte[2048]; // read in chunks of 2KB
+ int bytesRead;
+ while ((bytesRead = songCopy.DataStream.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ Google.Protobuf.ByteString bufferByteString = Google.Protobuf.ByteString.CopyFrom(buffer);
+ await responseStream.WriteAsync(new Chunk { Content = bufferByteString });
+ }
+ Console.WriteLine("Done sending file");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Exception caught while sending audio file: " + ex.Message);
}
- Console.WriteLine("Done sending file");
}
}
}
\ No newline at end of file
diff --git a/Aurora/RemoteImpl/RemoteSyncImpl.cs b/Aurora/RemoteImpl/RemoteSyncImpl.cs
index 0a8f7d9..faa6a0f 100644
--- a/Aurora/RemoteImpl/RemoteSyncImpl.cs
+++ b/Aurora/RemoteImpl/RemoteSyncImpl.cs
@@ -19,18 +19,22 @@ namespace Aurora.RemoteImpl
Grpc.Core.IServerStreamWriter responseStream,
Grpc.Core.ServerCallContext context)
{
- bool songIsPlaying = true;
- PlaybackStateChangedEventHandler playbackStateChanged = (sender, e) =>
+ bool continueSync = true;
+ string currentId = PlayerService.Instance.CurrentMedia.Id;
+ MediaChangedEventHandler mediaChanged = (sender, e) =>
{
- songIsPlaying = false;
+ if (e.NewId != currentId)
+ {
+ continueSync = false;
+ }
};
- PlayerService.Instance.PlaybackStateChanged += playbackStateChanged;
+ PlayerService.Instance.MediaChanged += mediaChanged;
- while (songIsPlaying)
+ while (continueSync)
{
DateTime time = Utils.TimeUtils.GetNetworkTime();
- float position = PlayerService.Instance.CurrentMediaTime;
+ float position = PlayerService.Instance.CurrentMediaPosition;
float length = PlayerService.Instance.CurrentMediaLength;
float trackTime = length * position;
@@ -41,7 +45,8 @@ namespace Aurora.RemoteImpl
ServerTime = time.Ticks
};
await responseStream.WriteAsync(sync);
- await Task.Delay(10000);
+ Console.WriteLine("Sent Sync");
+ await Task.Delay(5000);
}
}
}
diff --git a/Aurora/Services/PlayerService/MediaChangedEvent.cs b/Aurora/Services/PlayerService/MediaChangedEvent.cs
index 868edf3..e78ce14 100644
--- a/Aurora/Services/PlayerService/MediaChangedEvent.cs
+++ b/Aurora/Services/PlayerService/MediaChangedEvent.cs
@@ -8,10 +8,12 @@ namespace Aurora.Services.PlayerService
public class MediaChangedEventArgs : EventArgs
{
public BaseMetadata NewMetadata { get; private set; }
+ public string NewId { get; private set; }
- public MediaChangedEventArgs(BaseMetadata metadata)
+ public MediaChangedEventArgs(string id, BaseMetadata metadata)
{
NewMetadata = metadata;
+ NewId = id;
}
}
}
\ No newline at end of file
diff --git a/Aurora/Services/PlayerService/PlayerService.cs b/Aurora/Services/PlayerService/PlayerService.cs
index 6e12539..baf6d7a 100644
--- a/Aurora/Services/PlayerService/PlayerService.cs
+++ b/Aurora/Services/PlayerService/PlayerService.cs
@@ -41,7 +41,7 @@ namespace Aurora.Services.PlayerService
{
get
{
- return this._currentMedia == null;
+ return this._currentMedia != null;
}
}
@@ -50,7 +50,12 @@ namespace Aurora.Services.PlayerService
return _currentMedia == media;
}
- public float CurrentMediaTime
+ public BaseMedia CurrentMedia
+ {
+ get { return _currentMedia; }
+ }
+
+ public float CurrentMediaPosition
{
get
{
@@ -85,7 +90,7 @@ namespace Aurora.Services.PlayerService
if (MediaChanged != null)
{
- MediaChanged.Invoke(this, new MediaChangedEventArgs(_currentMedia.Metadata));
+ MediaChanged.Invoke(this, new MediaChangedEventArgs(_currentMedia.Id, _currentMedia.Metadata));
}
}
@@ -103,43 +108,53 @@ namespace Aurora.Services.PlayerService
if (_currentMedia is RemoteAudio)
{
RemoteAudio media = _currentMedia as RemoteAudio;
- RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient = media.RemoteSyncClient;
-
- //Sync playback in a separate task
- //Task completes when host stops syncing (when a song is complete)
- Task syncTask = new Task(async () =>
+ if (!media.FromHost)
{
- CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
- using (AsyncServerStreamingCall syncStream = _remoteSyncClient
- .GetMediaSync(new Proto.General.Empty()))
+ RemoteSyncService.RemoteSyncServiceClient _remoteSyncClient = media.RemoteSyncClient;
+
+ //Sync playback in a separate task
+ //Task completes when host stops syncing (when a song is complete)
+ Task syncTask = new Task(async () =>
{
- try
+ CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
+ using (AsyncServerStreamingCall syncStream = _remoteSyncClient
+ .GetMediaSync(new Proto.General.Empty()))
{
- while (await syncStream.ResponseStream.MoveNext(cancellationTokenSource.Token))
+ try
{
- Sync sync = new Sync(syncStream.ResponseStream.Current);
- if (sync != null)
+ while (await syncStream.ResponseStream.MoveNext(cancellationTokenSource.Token))
{
- //Adjust position based on sync
- DateTime localTime = Utils.TimeUtils.GetNetworkTime();
- //Get offset converted to milliseconds
- float offset = ((localTime.Ticks - sync.ServerTime) * 100) / (1000 * 1000);
+ Sync sync = new Sync(syncStream.ResponseStream.Current);
+ if (sync != null)
+ {
+ //Adjust position based on sync
+ DateTime localTime = Utils.TimeUtils.GetNetworkTime();
+ //Get offset converted to milliseconds
+ float offset = ((localTime.Ticks - sync.ServerTime) * 100) / (1000 * 1000);
- float length = CurrentMediaLength;
- float position = (sync.TrackTime + offset) / length;
+ float length = CurrentMediaLength;
+ float newPosition = (sync.TrackTime + offset) / length;
- _mediaPlayer.Position = position;
+ //Adjust position if greater than 10 percent difference
+ float oldPosition = _mediaPlayer.Position;
+ if (newPosition - oldPosition > 0.001 ||
+ newPosition - oldPosition < -0.001)
+ {
+ _mediaPlayer.Position = newPosition;
+ Console.WriteLine("Audio synced");
+ }
+ }
}
}
+ catch (Exception ex)
+ {
+ Console.WriteLine("Exception caught while attempting to sync: " + ex.Message);
+ }
}
- catch (Exception ex)
- {
- Console.WriteLine("Exception caught while attempting to sync: " + ex.Message);
- }
- }
- });
+ });
- syncTask.Start();
+ syncTask.Start();
+ }
}
if (PlaybackStateChanged != null)