Esempio n. 1
0
        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;
        }
Esempio n. 2
0
		/// <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);
        }