コード例 #1
0
        /// <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;
            this.Log("Yay! Sample requested!");
            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.Log("Reporting GetSampleCompleted called");
                this.ReportGetSampleCompleted(audioSample);
            }
        }
コード例 #2
0
        /// <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();

            wfx.FormatTag = 0x55;//mp3
            wfx.Channels = (short)((mpegLayer3Frame.Channels == Channel.SingleChannel) ? 1 : 2);
            wfx.SamplesPerSec = mpegLayer3Frame.SamplingRate;
            wfx.AverageBytesPerSecond = mpegLayer3Frame.Bitrate / 8;
            wfx.BlockAlign = 1;
            wfx.BitsPerSample = 0;
            wfx.ExtraDataSize = 12;
            this.MpegLayer3WaveFormat.WaveFormatExtensible = wfx;

            this.MpegLayer3WaveFormat.Id = 1;
            this.MpegLayer3WaveFormat.BitratePaddingMode = 0;
            this.MpegLayer3WaveFormat.FramesPerBlock = 1;
            this.MpegLayer3WaveFormat.BlockSize = (short)mpegLayer3Frame.FrameSize;
            this.MpegLayer3WaveFormat.CodecDelay = 0;

            var hexCodecData = MpegLayer3WaveFormat.ToHexString();
            mediaStreamAttributes[MediaStreamAttributeKeys.CodecPrivateData] = hexCodecData;
            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.Log("Reporting open Media completed");
            this.ReportOpenMediaCompleted(mediaSourceAttributes, mediaStreamDescriptions);

            this.currentFrame = mpegLayer3Frame;
            this.currentFrameStartPosition = MpegFrame.FrameHeaderSize;
        }
コード例 #3
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="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;

                //TODO:SS: try remove this Task.Run (we probably dont need threads in BG agent)
                // 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");
        }