private static IMFTranscodeProfile CreateProfile(AudioFormat audioOutput, VideoFormat videoOutput, Guid containerType)
        {
            IMFTranscodeProfile profile = null;

            // Create a transcode profile
            MFHelper.MFCreateTranscodeProfile(out profile);

            // Create and set the audio attributes
            profile.SetAudioAttributes(SimpleFastEncode.CreateAudioAttributes(audioOutput));

            // Create and set the video attributes
            profile.SetVideoAttributes(SimpleFastEncode.CreateVideoAttributes(videoOutput));

            // Create the container attributes
            IMFAttributes containerAttributes = null;

            MFHelper.MFCreateAttributes(out containerAttributes, 2);
            containerAttributes.SetUINT32(new Guid(Consts.MF_TRANSCODE_TOPOLOGYMODE), 1);
            containerAttributes.SetGUID(new Guid(Consts.MF_TRANSCODE_CONTAINERTYPE), containerType);

            // Set them in the transcoding profile
            profile.SetContainerAttributes(containerAttributes);

            return(profile);
        }
        private void NegotiateStream(uint streamIndex, Guid majorType, Guid[] possibleSubtypes)
        {
            IMFMediaType outputMediaType = null;
            Exception    lastException   = null;
            bool         foundValidType  = false;

            foreach (Guid subType in possibleSubtypes)
            {
                try
                {
                    // Create a partial media type
                    MFHelper.MFCreateMediaType(out outputMediaType);
                    outputMediaType.SetGUID(new Guid(Consts.MF_MT_MAJOR_TYPE), majorType);
                    outputMediaType.SetGUID(new Guid(Consts.MF_MT_SUBTYPE), subType);

                    // Set it as the current media type in source reader
                    this.sourceReader.SetCurrentMediaType(streamIndex, IntPtr.Zero, outputMediaType);

                    // Get the full media type from Source Reader
                    this.sourceReader.GetCurrentMediaType(streamIndex, out outputMediaType);

                    // Set it as the input media type in Sink Writer
                    this.sinkWriter.SetInputMediaType(this.streamsInfo[streamIndex].OutputStreamIndex, outputMediaType, null);

                    // If no exceptions were thrown a suitable media type was found
                    foundValidType = true;

                    // Break from the loop so no more media types are tried
                    break;
                }
                catch (Exception ex)
                {
                    // Something went wrong, the loop will continue and the next type will be tried
                    lastException = ex;
                }
            }

            // If FormatFound is false no suitable type was found and the operation can't continue
            if (foundValidType == false)
            {
                if (lastException != null)
                {
                    throw new Exception(lastException.Message, lastException);
                }
                else
                {
                    throw new Exception("Unknown error occurred");
                }
            }
        }
        private static IMFAttributes CreateAudioAttributes(AudioFormat audioOutput)
        {
            uint   codecConfig         = 0;
            object supportedAttributes = null;

            // Create the audio attributes
            IMFAttributes audioAttributes = null;

            // Generate the audio media type
            uint elementsNumber   = 0;
            uint selectedType     = 0;
            int  avgBitrateDiff   = int.MaxValue;
            uint avgBytePerSecond = uint.MaxValue;

            // Get the available audio ouput types for the required sub type
            IMFCollection availableTypes = null;

            MFHelper.MFTranscodeGetAudioOutputAvailableTypes(audioOutput.Subtype, (uint)Enums.MFT_ENUM_FLAG.MFT_ENUM_FLAG_ALL, codecConfig, out availableTypes);

            // Get the number of types
            availableTypes.GetElementCount(out elementsNumber);

            // Find the best match for our needs
            for (uint elementIndex = 0; elementIndex < elementsNumber; elementIndex++)
            {
                // Get the next element
                availableTypes.GetElement(elementIndex, out supportedAttributes);
                audioAttributes = (IMFAttributes)supportedAttributes;

                // Get the byte per second
                audioAttributes.GetUINT32(new Guid(Consts.MF_MT_AUDIO_AVG_BYTES_PER_SECOND), out avgBytePerSecond);

                // If this is better than the last one found remember the index
                if (Math.Abs((int)avgBytePerSecond - (int)audioOutput.AvgBytePerSecond) < avgBitrateDiff)
                {
                    selectedType   = elementIndex;
                    avgBitrateDiff = Math.Abs((int)avgBytePerSecond - (int)audioOutput.AvgBytePerSecond);
                }

                audioAttributes = null;
            }

            // Get the best audio type found
            availableTypes.GetElement(selectedType, out supportedAttributes);
            audioAttributes = (IMFAttributes)supportedAttributes;

            return(audioAttributes);
        }
        private IMFMediaType CreateTargetVideoMediaType()
        {
            IMFMediaType mediaType = null;

            MFHelper.MFCreateMediaType(out mediaType);

            mediaType.SetGUID(new Guid(Consts.MF_MT_MAJOR_TYPE), new Guid(Consts.MFMediaType_Video));

            mediaType.SetGUID(new Guid(Consts.MF_MT_SUBTYPE), this.videoOutput.Subtype);
            mediaType.SetUINT64(new Guid(Consts.MF_MT_FRAME_SIZE), this.videoOutput.FrameSize.Packed);
            mediaType.SetUINT64(new Guid(Consts.MF_MT_FRAME_RATE), this.videoOutput.FrameRate.Packed);
            mediaType.SetUINT64(new Guid(Consts.MF_MT_PIXEL_ASPECT_RATIO), this.videoOutput.PixelAspectRatio.Packed);
            mediaType.SetUINT32(new Guid(Consts.MF_MT_AVG_BITRATE), this.videoOutput.AvgBitRate);
            mediaType.SetUINT32(new Guid(Consts.MF_MT_INTERLACE_MODE), this.videoOutput.InterlaceMode);

            return(mediaType);
        }
        private static IMFAttributes CreateVideoAttributes(VideoFormat videoOutput)
        {
            IMFAttributes videoAttributes = null;

            MFHelper.MFCreateAttributes(out videoAttributes, 0);
            videoAttributes.SetGUID(new Guid(Consts.MF_MT_SUBTYPE), videoOutput.Subtype);

            PackedINT32 pixelAspectRatio = new PackedINT32(1, 1);

            // Set the argument attributes
            videoAttributes.SetUINT64(new Guid(Consts.MF_MT_FRAME_RATE), videoOutput.FrameRate.Packed);
            videoAttributes.SetUINT64(new Guid(Consts.MF_MT_FRAME_SIZE), videoOutput.FrameSize.Packed);
            videoAttributes.SetUINT64(new Guid(Consts.MF_MT_PIXEL_ASPECT_RATIO), pixelAspectRatio.Packed);
            videoAttributes.SetUINT32(new Guid(Consts.MF_MT_INTERLACE_MODE), videoOutput.InterlaceMode);
            videoAttributes.SetUINT32(new Guid(Consts.MF_MT_AVG_BITRATE), videoOutput.AvgBitRate);

            return(videoAttributes);
        }
        private void CreateReaderAndWriter()
        {
            object        sourceReaderObject = null;
            object        sinkWriterObject   = null;
            IMFAttributes attributes         = null;

            // Create the class factory
            IMFReadWriteClassFactory factory = (IMFReadWriteClassFactory)(new MFReadWriteClassFactory());

            // Create the attributes
            MFHelper.MFCreateAttributes(out attributes, 1);
            attributes.SetUINT32(new Guid(Consts.MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS), 1);

            // Use the factory to create the Source Reader
            factory.CreateInstanceFromURL(new Guid(CLSID.MFSourceReader), this.sourceFilename, attributes, new Guid(IID.IMFSourceReader), out sourceReaderObject);
            this.sourceReader = (IMFSourceReader)sourceReaderObject;

            // Use the factory to create the Sink Writer
            factory.CreateInstanceFromURL(new Guid(CLSID.MFSinkWriter), this.targetFilename, attributes, new Guid(IID.IMFSinkWriter), out sinkWriterObject);
            this.sinkWriter = (IMFSinkWriter)sinkWriterObject;
        }
        private IMFMediaType CreateTargetAudioMediaType()
        {
            IMFMediaType mediaType = null;

            if (audioOutput.Subtype.Equals(new Guid(Consts.MFAudioFormat_AAC)))
            {
                // Create the AAC media type
                MFHelper.MFCreateMediaType(out mediaType);

                mediaType.SetGUID(new Guid(Consts.MF_MT_MAJOR_TYPE), new Guid(Consts.MFMediaType_Audio));

                mediaType.SetGUID(new Guid(Consts.MF_MT_SUBTYPE), this.audioOutput.Subtype);
                mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_AVG_BYTES_PER_SECOND), this.audioOutput.AvgBytePerSecond);
                mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_NUM_CHANNELS), this.audioOutput.NumOfChannels);
                mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_SAMPLES_PER_SECOND), this.audioOutput.SamplesPerSecond);
                mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_BLOCK_ALIGNMENT), this.audioOutput.BlockAlignment);
                mediaType.SetUINT32(new Guid(Consts.MF_MT_AUDIO_BITS_PER_SAMPLE), this.audioOutput.BitsPerSample);
            }
            else
            {
                // Create the WMA media type
                uint   codecConfig         = 0;
                uint   elementsNumber      = 0;
                uint   selectedType        = 0;
                int    avgBitrateDiff      = int.MaxValue;
                uint   avgBytePerSecond    = uint.MaxValue;
                object supportedAttributes = null;

                // Get the available audio ouput types for the required sub type
                IMFCollection availableTypes = null;
                MFHelper.MFTranscodeGetAudioOutputAvailableTypes(audioOutput.Subtype, (uint)Enums.MFT_ENUM_FLAG.MFT_ENUM_FLAG_ALL, codecConfig, out availableTypes);

                // Get the number of types
                availableTypes.GetElementCount(out elementsNumber);

                for (uint elementIndex = 0; elementIndex < elementsNumber; elementIndex++)
                {
                    // Get the next element
                    availableTypes.GetElement(elementIndex, out supportedAttributes);
                    mediaType = (IMFMediaType)supportedAttributes;

                    // Get the byte per second
                    mediaType.GetUINT32(new Guid(Consts.MF_MT_AUDIO_AVG_BYTES_PER_SECOND), out avgBytePerSecond);

                    // If this is better than the last one found remember the index
                    if (Math.Abs((int)avgBytePerSecond - (int)audioOutput.AvgBytePerSecond) < avgBitrateDiff)
                    {
                        selectedType   = elementIndex;
                        avgBitrateDiff = Math.Abs((int)avgBytePerSecond - (int)audioOutput.AvgBytePerSecond);
                    }

                    mediaType = null;
                }

                // Get the best audio type found
                availableTypes.GetElement(selectedType, out supportedAttributes);
                mediaType = (IMFMediaType)supportedAttributes;
            }

            return(mediaType);
        }
        /// <summary>
        ///     Starts the asychronous encode operation
        /// </summary>
        /// <param name="inputURL">Source filename</param>
        /// <param name="outputURL">Targe filename</param>
        /// <param name="audioOutput">Audio format that will be used for audio streams</param>
        /// <param name="videoOutput">Video format that will be used for video streams</param>
        /// <param name="startPosition">Starting position of the contet</param>
        /// <param name="endPosition">Position where the new content will end</param>
        public void Encode(string inputURL, string outputURL, AudioFormat audioOutput, VideoFormat videoOutput, ulong startPosition, ulong endPosition)
        {
            // If busy with other operation ignore and return
            if (this.IsBusy())
            {
                return;
            }

            try
            {
                this.presentationClock = null;
                this.startPosition     = startPosition;
                this.endPosition       = endPosition;

                object objectSource = null;

                // Create the media source using source resolver and the input URL
                uint objectType = default(uint);
                this.mediaSource = null;

                // Init source resolver
                IMFSourceResolver sourceResolver = null;
                MFHelper.MFCreateSourceResolver(out sourceResolver);

                sourceResolver.CreateObjectFromURL(inputURL, Consts.MF_RESOLUTION_MEDIASOURCE, null, out objectType, out objectSource);

                this.mediaSource = (IMFMediaSource)objectSource;

                // Create the media session using a global start time so MF_TOPOLOGY_PROJECTSTOP can be used to stop the session
                this.mediaSession = null;
                IMFAttributes mediaSessionAttributes = null;

                MFHelper.MFCreateAttributes(out mediaSessionAttributes, 1);
                mediaSessionAttributes.SetUINT32(new Guid(Consts.MF_SESSION_GLOBAL_TIME), 1);

                MFHelper.MFCreateMediaSession(mediaSessionAttributes, out this.mediaSession);

                // Create the event handler
                AsyncEventHandler mediaEventHandler = new AsyncEventHandler(this.mediaSession);
                mediaEventHandler.MediaEvent += this.MediaEvent;

                // Get the stream descriptor
                IMFPresentationDescriptor presentationDescriptor = null;
                mediaSource.CreatePresentationDescriptor(out presentationDescriptor);

                // Get the duration
                presentationDescriptor.GetUINT64(new Guid(Consts.MF_PD_DURATION), out this.duration);
                IMFTranscodeProfile transcodeProfile = null;

                Guid containerType = new Guid(Consts.MFTranscodeContainerType_MPEG4);
                if (outputURL.EndsWith(".wmv", StringComparison.OrdinalIgnoreCase) || outputURL.EndsWith(".wma", StringComparison.OrdinalIgnoreCase))
                {
                    containerType = new Guid(Consts.MFTranscodeContainerType_ASF);
                }

                // Generate the transcoding profile
                transcodeProfile = SimpleFastEncode.CreateProfile(audioOutput, videoOutput, containerType);

                // Create the MF topology using the profile
                IMFTopology topology = null;
                MFHelper.MFCreateTranscodeTopology(this.mediaSource, outputURL, transcodeProfile, out topology);

                // Set the end position
                topology.SetUINT64(new Guid(Consts.MF_TOPOLOGY_PROJECTSTART), 0);
                topology.SetUINT64(new Guid(Consts.MF_TOPOLOGY_PROJECTSTOP), (endPosition == 0) ? this.duration : endPosition);

                // Set the session topology
                this.mediaSession.SetTopology((uint)Enums.MFSESSION_SETTOPOLOGY_FLAGS.None, topology);
            }
            catch (Exception ex)
            {
                this.mediaSession = null;

                // Fire the EncodeError event
                if (this.EncodeError != null)
                {
                    this.EncodeError(new Exception(ex.Message, ex));
                }
            }
        }