public MpegFrame(Stream stream, byte[] data) { this.frameHeader = new byte[4]; if (data != null) { for (int index = 0; index < this.frameHeader.Length; ++index) { this.frameHeader[index] = data[index]; } } else if (stream.Read(this.frameHeader, 0, 4) != 4) { goto label_7; } if (BitTools.MaskBits(this.frameHeader, 0, 11) == 2047) { this.Version = MpegFrame.ParseVersion(this.frameHeader); this.Layer = MpegFrame.ParseLayer(this.frameHeader); this.IsProtected = BitTools.MaskBits(this.frameHeader, 15, 1) != 1; this.BitrateIndex = BitTools.MaskBits(this.frameHeader, 16, 4); this.SamplingRateIndex = BitTools.MaskBits(this.frameHeader, 20, 2); this.Padding = BitTools.MaskBits(this.frameHeader, 22, 1); this.Channels = MpegFrame.ParseChannel(this.frameHeader); return; } label_7: this.frameHeader = (byte[])null; }
/// <summary> /// Read off the Id3Data from the stream and return the first MpegFrame of the audio stream. /// This assumes that the first bit of data is either an ID3 segment or an MPEG segment. Calls a separate thread /// to read past ID3v2 data. /// </summary> /// <param name="randomAccessStream"></param> /// <returns>The first MpegFrame.</returns> public static MpegFrame ReadPastId3V2Tags(this IRandomAccessStream randomAccessStream) { MpegFrame mpegFrame = null; var audioStream = randomAccessStream.AsStreamForRead(); if (audioStream != null) { // Read and (throw out) any Id3 data if present. byte[] data = new byte[10]; audioStream.Position = 0; if (audioStream.Read(data, 0, 3) != 3) { goto cleanup; } if (data[0] == 73 /* I */ && data[1] == 68 /* D */ && data[2] == 51 /* 3 */) { // Need to update to read the is footer present flag and account for its 10 bytes if needed. if (audioStream.Read(data, 3, 7) != 7) { goto cleanup; } int id3Size = BitTools.ConvertSyncSafeToInt32(data, 6); int bytesRead = 0; // Read through the ID3 Data tossing it out.) while (id3Size > 0) { bytesRead = (id3Size - buffer.Length > 0) ? audioStream.Read(buffer, 0, buffer.Length) : audioStream.Read(buffer, 0, id3Size); id3Size -= bytesRead; } mpegFrame = new MpegFrame(audioStream); } else { // No ID3 tag present, presumably this is streaming and we are starting right at the Mp3 data. // Assume the stream isn't seekable. if (audioStream.Read(data, 3, 1) != 1) { goto cleanup; } mpegFrame = new MpegFrame(audioStream, data); } } return mpegFrame; // Cleanup and quit if you couldn't even read the initial data for some reason. cleanup: throw new Exception("Could not read intial audio stream data"); }
/// <summary> /// Callback which handles setting up an MSS once the first MpegFrame after Id3v2 data has been read. /// </summary> /// <param name="mpegLayer3Frame"> First MpegFrame</param> /// <param name="mediaStreamAttributes">Empty dictionary for MediaStreamAttributes</param> /// <param name="mediaStreamDescriptions">Empty dictionary for MediaStreamDescriptions</param> /// <param name="mediaSourceAttributes">Empty dictionary for MediaSourceAttributes</param> private void ReadPastId3v2TagsCallback( MpegFrame mpegLayer3Frame, Dictionary<MediaStreamAttributeKeys, string> mediaStreamAttributes, List<MediaStreamDescription> mediaStreamDescriptions, Dictionary<MediaSourceAttributesKeys, string> mediaSourceAttributes) { if (mpegLayer3Frame.FrameSize <= 0) { throw new InvalidOperationException("MpegFrame's FrameSize cannot be negative"); } // Initialize the Mp3 data structures used by the Media pipeline with state from the first frame. WaveFormatExtensible wfx = new WaveFormatExtensible(); this.MpegLayer3WaveFormat = new MpegLayer3WaveFormat(); this.MpegLayer3WaveFormat.WaveFormatExtensible = wfx; this.MpegLayer3WaveFormat.WaveFormatExtensible.FormatTag = 85; this.MpegLayer3WaveFormat.WaveFormatExtensible.Channels = (short)((mpegLayer3Frame.Channels == Channel.SingleChannel) ? 1 : 2); this.MpegLayer3WaveFormat.WaveFormatExtensible.SamplesPerSec = mpegLayer3Frame.SamplingRate; this.MpegLayer3WaveFormat.WaveFormatExtensible.AverageBytesPerSecond = mpegLayer3Frame.Bitrate / 8; this.MpegLayer3WaveFormat.WaveFormatExtensible.BlockAlign = 1; this.MpegLayer3WaveFormat.WaveFormatExtensible.BitsPerSample = 0; this.MpegLayer3WaveFormat.WaveFormatExtensible.ExtraDataSize = 12; this.MpegLayer3WaveFormat.Id = 1; this.MpegLayer3WaveFormat.BitratePaddingMode = 0; this.MpegLayer3WaveFormat.FramesPerBlock = 1; this.MpegLayer3WaveFormat.BlockSize = (short)mpegLayer3Frame.FrameSize; this.MpegLayer3WaveFormat.CodecDelay = 0; mediaStreamAttributes[MediaStreamAttributeKeys.CodecPrivateData] = this.MpegLayer3WaveFormat.ToHexString(); this.audioStreamDescription = new MediaStreamDescription(MediaStreamType.Audio, mediaStreamAttributes); mediaStreamDescriptions.Add(this.audioStreamDescription); this.trackDuration = new TimeSpan(0, 0, (int)(this.audioStreamLength / MpegLayer3WaveFormat.WaveFormatExtensible.AverageBytesPerSecond)); mediaSourceAttributes[MediaSourceAttributesKeys.Duration] = this.trackDuration.Ticks.ToString(CultureInfo.InvariantCulture); if (this.audioStream.CanSeek) { mediaSourceAttributes[MediaSourceAttributesKeys.CanSeek] = "1"; } else { mediaSourceAttributes[MediaSourceAttributesKeys.CanSeek] = "0"; } // Report that the Mp3MediaStreamSource has finished initializing its internal state and can now // pass in Mp3 Samples. this.ReportOpenMediaCompleted(mediaSourceAttributes, mediaStreamDescriptions); this.currentFrame = mpegLayer3Frame; this.currentFrameStartPosition = MpegFrame.FrameHeaderSize; }
/// <summary> /// Parses the next sample from the requested stream and then calls ReportGetSampleCompleted /// to inform its parent MediaElement of the next sample. /// </summary> /// <param name="mediaStreamType"> /// Should always be Audio for this MediaStreamSource. /// </param> protected override void GetSampleAsync(MediaStreamType mediaStreamType) { Dictionary<MediaSampleAttributeKeys, string> emptyDict = new Dictionary<MediaSampleAttributeKeys, string>(); MediaStreamSample audioSample = null; if (this.currentFrame != null) { // Calculate our current position based on the stream's length //// double ratio = (double)this.currentFrameStartPosition / (double)this.audioStreamLength; //// TimeSpan currentPosition = new TimeSpan((long)(this.trackDuration.Ticks * ratio)); // Calculate our current position instead based on the bitrate of the stream (more accurate?) double position = (double)this.currentFrameStartPosition / (double)this.currentFrame.Bitrate; TimeSpan currentPosition = TimeSpan.FromSeconds(position * 8 /* bits per Byte */); // Create a MemoryStream to hold the bytes // FrameSize includes the frame header which we've already read from the previous iteration, so just copy the // header, and then read the remaining bytes this.currentFrame.CopyHeader(buffer); int audioSampleSize = this.currentFrame.FrameSize - MpegFrame.FrameHeaderSize; int c = this.audioStream.Read(buffer, MpegFrame.FrameHeaderSize, audioSampleSize); if (c != audioSampleSize) { // Ran out of bytes trying to read MP3 frame. this.currentFrame = null; audioSample = new MediaStreamSample(this.audioStreamDescription, null, 0, 0, 0, emptyDict); this.ReportGetSampleCompleted(audioSample); return; } this.currentFrameStartPosition += c; using (MemoryStream audioFrameStream = new MemoryStream(buffer)) { // Return the next sample in the stream audioSample = new MediaStreamSample(this.audioStreamDescription, audioFrameStream, 0, this.currentFrame.FrameSize, currentPosition.Ticks, emptyDict); this.ReportGetSampleCompleted(audioSample); // Grab the next frame MpegFrame nextFrame = new MpegFrame(this.audioStream); if ( (nextFrame.Version == 1 || nextFrame.Version == 2) && nextFrame.Layer == 3) { this.currentFrameStartPosition += MpegFrame.FrameHeaderSize; this.currentFrame = nextFrame; } else { this.currentFrame = null; } } } else { // We're near the end of the file, or we got an irrecoverable error. // Return a null stream which tells the MediaStreamSource & MediaElement to shut down audioSample = new MediaStreamSample(this.audioStreamDescription, null, 0, 0, 0, emptyDict); this.ReportGetSampleCompleted(audioSample); } }
/// <summary> /// Read off the Id3Data from the stream and return the first MpegFrame of the audio stream. /// This assumes that the first bit of data is either an ID3 segment or an MPEG segment. Calls a separate thread /// to read past ID3v2 data. /// </summary> /// <param name="callback"> /// Callback that can process the first MpegFrame and set up the MSS. Called back once the thread has skipped /// over all of the Id3V2 tags. /// </param> public void ReadPastId3V2Tags(Action<MpegFrame> callback) { /* * Since this code assumes that the first bit of data is either an ID3 segment or an MPEG segment it could * get into trouble. Should probably do something a bit more robust at some point. */ MpegFrame mpegFrame; // Read and (throw out) any Id3 data if present. byte[] data = new byte[10]; if (this.audioStream.Read(data, 0, 3) != 3) { goto cleanup; } if (data[0] == 73 /* I */ && data[1] == 68 /* D */ && data[2] == 51 /* 3 */) { // Need to update to read the is footer present flag and account for its 10 bytes if needed. if (this.audioStream.Read(data, 3, 7) != 7) { goto cleanup; } int id3Size = BitTools.ConvertSyncSafeToInt32(data, 6); int bytesRead = 0; ThreadPool.QueueUserWorkItem(state => { // Read through the ID3 Data tossing it out.) while (id3Size > 0) { bytesRead = (id3Size - buffer.Length > 0) ? this.audioStream.Read(buffer, 0, buffer.Length) : this.audioStream.Read(buffer, 0, id3Size); id3Size -= bytesRead; } mpegFrame = new MpegFrame(this.audioStream); callback(mpegFrame); }); } else { // No ID3 tag present, presumably this is streaming and we are starting right at the Mp3 data. // Assume the stream isn't seekable. if (this.audioStream.Read(data, 3, 1) != 1) { goto cleanup; } mpegFrame = new MpegFrame(this.audioStream, data); callback(mpegFrame); } return; // Cleanup and quit if you couldn't even read the initial data for some reason. cleanup: throw new Exception("Could not read intial audio stream data"); }
protected virtual void Dispose(bool disposing) { if (disposing) { this.mf = null; } this.s.Close(); }
public void Setup() { this.s.Position = 0; this.mf = new MpegFrame(this.s); this.s.Seek(0, SeekOrigin.Begin); this.s.Read(headerData, 0, 4); this.mf2 = new MpegFrame(this.s, MpegFrameTests.headerData); }