/// <summary> /// Determines whether the specified Object is equal to the current Object /// </summary> /// <param name="obj">The object to compare with the current object.</param> /// <returns>true if the specified Object is equal to the current Object; otherwise, false.</returns> public override bool Equals(object obj) { // We need value-type semantics. ShoutcastMetadata other = obj as ShoutcastMetadata; if (other == null) { return(false); } return(this.Title.Equals(other.Title) && this.Url.Equals(other.Url)); }
/// <summary> /// Reads or Peeks data from the circular buffer. /// </summary> /// <param name="buffer">Buffer in which to put the read or peeked data.</param> /// <param name="count">Number of bytes to read or peek.</param> /// <param name="shouldPeek">true if the data should be peeked from the circular buffer, otherwise the data is read from the circular buffer.</param> private void ReadOrPeekBuffer(byte[] buffer, int count, bool shouldPeek) { if (buffer == null) { throw new ArgumentNullException("buffer"); } if (this.ReadIncludesMetadata(count)) { // Read to metadata chunk int metadataOffset = this.icyMetadata - this.metadataCount; byte[] metadataSizeBuffer = new byte[metadataOffset + 1]; int bytesRead = 0; int metadataSize = 0; lock (this.syncRoot) { bytesRead = this.circularBuffer.Peek(metadataSizeBuffer, 0, metadataOffset + 1); } if (bytesRead != (metadataOffset + 1)) { // We didn't read enough. throw new IndexOutOfRangeException("metadataSize buffer not filled."); } metadataSize = metadataSizeBuffer[metadataOffset]; int metadataByteCount = metadataSize * 16; int numberOfBytesAfterMetadata = count - metadataOffset; // We need the size of the pre-metadata bytes + metadata size byte + metadata byte count + remaining data bytes. byte[] metadataBuffer = new byte[metadataOffset + 1 + metadataByteCount + numberOfBytesAfterMetadata]; lock (this.syncRoot) { // Here is where we either Get() or Peek(). The work before is just to get the right size. if (shouldPeek) { bytesRead = this.circularBuffer.Peek(metadataBuffer, 0, metadataBuffer.Length); } else { bytesRead = this.circularBuffer.Get(metadataBuffer, 0, metadataBuffer.Length); this.metadataCount = numberOfBytesAfterMetadata; } } // We are going to throw the metadata away here, as it will be read again. // Copy from beginning to metadata offset Array.Copy(metadataBuffer, 0, buffer, 0, metadataOffset); // Copy after metadata Array.Copy(metadataBuffer, metadataOffset + 1 + metadataByteCount, buffer, metadataOffset, numberOfBytesAfterMetadata); // Only change the metadata when we ACTUALLY read. if ((!shouldPeek) && (metadataSize != 0)) { string newMetadata = this.metadataEncoding.GetString(metadataBuffer, metadataOffset + 1, metadataByteCount) ?? string.Empty; // TODO - Should we fire this every time the metadata changes, or whenever it is parsed. // See if we need to fire the metadata changed event if (string.Compare(this.currentMetadata, newMetadata) != 0) { this.currentMetadata = newMetadata; // We need to set the current metadata on the MSS so it is always available. ShoutcastMetadata metadata = new ShoutcastMetadata(this.currentMetadata); Interlocked.Exchange<ShoutcastMetadata>(ref this.mediaStreamSource.currentMetadata, metadata); // Since MediaElement can only be created on the UI thread, we will marshal this event over to the UI thread, plus this keeps us from blocking. Deployment.Current.Dispatcher.BeginInvoke(() => this.mediaStreamSource.OnMetadataChanged()); } } } else { int bytesRead; lock (this.syncRoot) { // Here is where we either Get() or Peek(). The work before is just to get the right size. if (shouldPeek) { bytesRead = this.circularBuffer.Peek(buffer, 0, count); } else { bytesRead = this.circularBuffer.Get(buffer, 0, count); this.metadataCount += bytesRead; } } } }