예제 #1
0
        public void ProcessingThread()
        {
            while (true)
            {
                m_SampleReady.WaitOne();

                IMFMediaBuffer pInput = null;

                try
                {
                    // Get the data buffer from the input sample.  If the sample has
                    // multiple buffers, you might be able to get (slightly) better
                    // performance processing each buffer in turn rather than forcing
                    // a new, full-sized buffer to get created.
                    m_hr = InputSample.ConvertToContiguousBuffer(out pInput);
                    if (Succeeded(m_hr))
                    {
                        m_hr = DoWork(pInput);
                    }
                }
                catch (Exception e)
                {
                    m_hr = (HResult)Marshal.GetHRForException(e);
                }
                finally
                {
                    // If (somewhere) there is .Net code that is holding on to an instance of
                    // the same buffer as pInput, this will yank the RCW out from underneath
                    // it, probably causing it to crash.  But if we don't release it, our memory
                    // usage explodes.
                    SafeRelease(pInput);
                    m_SampleDone.Set();
                }
            }
        }
예제 #2
0
        protected override HResult OnProcessOutput(ref MFTOutputDataBuffer pOutputSamples)
        {
            HResult hr = HResult.S_OK;

            if (pOutputSamples.pSample == IntPtr.Zero)
            {
                // Synchronous MFTs don't (by default) have an IMFAttributes.
                // So I'm putting the sample number on the actual sample, just
                // to have some place to put it.
                MFError throwonhr = InputSample.SetUINT32(m_SampleCountGuid, m_iSampleCount);

                m_iSampleCount++;

                // The output sample is the input sample.
                pOutputSamples.pSample = Marshal.GetIUnknownForObject(InputSample);

                // Release the current input sample so we can get another one.
                InputSample = null;
            }
            else
            {
                hr = HResult.E_INVALIDARG;
            }

            return(hr);
        }
예제 #3
0
        protected override HResult OnProcessInput()
        {
            HResult hr = HResult.S_OK;

            // While we accept types that *might* be interlaced, if we actually receive
            // an interlaced sample, reject it.
            if (m_MightBeInterlaced)
            {
                int ix;

                // Returns a bool: true = interlaced, false = progressive
                hr = InputSample.GetUINT32(MFAttributesClsid.MFSampleExtension_Interlaced, out ix);

                if (hr != HResult.S_OK || ix != 0)
                {
                    hr = HResult.E_FAIL;
                }
            }

            if (Succeeded(hr))
            {
                // Tell the background thread to start processing;
                m_SampleReady.Set();
            }

            return(hr);
        }
예제 #4
0
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// This is the routine that performs the transform. Assumes InputSample is set.
        ///
        /// An override of the abstract version in TantaMFTBase_Sync.
        /// </summary>
        /// <param name="outputSampleDataStruct">The structure to populate with output data.</param>
        /// <returns>S_Ok unless error.</returns>
        /// <history>
        ///    01 Nov 18  Cynic - Ported In
        /// </history>
        protected override HResult OnProcessOutput(ref MFTOutputDataBuffer outputSampleDataStruct)
        {
            HResult        hr = HResult.S_OK;
            IMFMediaBuffer outputMediaBuffer = null;

            // we are processing in place, the input sample is the output sample, the media buffer of the
            // input sample is the media buffer of the output sample.

            try
            {
                // Get the data buffer from the input sample. If the sample contains more than one buffer,
                // this method copies the data from the original buffers into a new buffer, and replaces
                // the original buffer list with the new buffer. The new buffer is returned in the inputMediaBuffer parameter.
                // If the sample contains a single buffer, this method returns a pointer to the original buffer.
                // In typical use, most samples do not contain multiple buffers.
                hr = InputSample.ConvertToContiguousBuffer(out outputMediaBuffer);
                if (hr != HResult.S_OK)
                {
                    throw new Exception("OnProcessOutput call to InputSample.ConvertToContiguousBuffer failed. Err=" + hr.ToString());
                }

                // now that we have an output buffer, do the work to implement the appropriate rotate mode.
                // Writing into outputMediaBuffer will write to the approprate location in the outputSample
                FlipImageInBuffer(outputMediaBuffer);

                // increment this for the client/transform communications demonstrator code
                m_FrameCount++;

                // Set status flags.
                outputSampleDataStruct.dwStatus = MFTOutputDataBufferFlags.None;
                // The output sample is the input sample. We get a new IUnknown for the Input
                // sample since we are going to release it below. The client will release this
                // new IUnknown
                outputSampleDataStruct.pSample = Marshal.GetIUnknownForObject(InputSample);
            }
            finally
            {
                // clean up
                SafeRelease(outputMediaBuffer);

                // Release the current input sample so we can get another one.
                // the act of setting it to null releases it because the property
                // is coded that way
                InputSample = null;
            }

            return(HResult.S_OK);
        }
예제 #5
0
 private void CheckBeltUsage()
 {
     if ((!UIUnityEvents.shouldBlockButtonInput && base.enabled) && !ConsoleWindow.IsVisible())
     {
         Inventory inventory = this.inventory;
         if (inventory != null)
         {
             InventoryHolder inventoryHolder = inventory.inventoryHolder;
             if (inventoryHolder != null)
             {
                 int beltNum = InputSample.PollItemButtons();
                 if (beltNum != -1)
                 {
                     inventoryHolder.BeltUse(beltNum);
                 }
             }
         }
     }
 }
예제 #6
0
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// Processes the input. Most of the transformation happens in OnProcessOutput
        /// so all we need to do here is check to see if the sample is interlaced and, if
        /// it is, discard it.
        ///
        /// Expects InputSample to be set.
        /// </summary>
        /// <returns>S_Ok or E_FAIL.</returns>
        /// <history>
        ///    01 Nov 18  Cynic - Ported over
        /// </history>
        protected override HResult OnProcessInput()
        {
            HResult hr = HResult.S_OK;

            // While we accept types that *might* be interlaced, if we actually receive
            // an interlaced sample, reject it.
            if (m_MightBeInterlaced == true)
            {
                int ix;

                // Returns a bool: true = interlaced, false = progressive
                hr = InputSample.GetUINT32(MFAttributesClsid.MFSampleExtension_Interlaced, out ix);
                if (hr != HResult.S_OK || ix != 0)
                {
                    hr = HResult.E_FAIL;
                }
            }

            return(hr);
        }
예제 #7
0
        /// <summary>
        /// This is the routine that performs the transform. Unless the sinkWriter object
        /// is set all we do is pass the sample on. If the sink writer object is set
        /// we give the sample to it for writing. There are two modes - one where we just
        /// give the sinkwriter the input sample and the other where we clone the input
        /// sample and rebase the timestamps.
        ///
        /// An override of the abstract version in MFTBase_Sync.
        /// </summary>
        /// <param name="pOutputSamples">The structure to populate with output values.</param>
        /// <returns>S_Ok unless error.</returns>
        protected override HResult OnProcessOutput(ref MFTOutputDataBuffer outputSampleDataStruct)
        {
            HResult        hr = HResult.S_OK;
            IMFMediaBuffer inputMediaBuffer = null;
            IMFSample      sinkWriterSample = null;
            IMFAttributes  sampleAttributes;

            // in this MFT we are processing in place, the input sample is the output sample, the media buffer of the
            // input sample is the media buffer of the output sample. Thats for the pipeline. If a sink writer exists
            // we also write the sample data out to the sink writer. This provides the effect of displaying on the
            // screen and simultaneously recording.
            // There are two ways the sink writer can be given the media sample data. It can just be given the
            // input sample directly or a copy of the sample can be made and that copy given to the sink writer.
            // There is also an additional complication - the sample has a timestamp and video cameras tend
            // to just use the current date and time as a timestamp. There are several reports that MP4 files
            // need to have their first frame starting at zero and then every subsequent frame adjusted to that
            // new base time. Certainly the Microsoft supplied example code (and see the
            // CaptureToFileViaReaderWriter also) take great care to do this. This requirement does not
            // seem to exist - my tests indicate it is not necessary to start from 0 in the mp4 file. Maybe the
            // Sink Writer has been improved and now does this automatically. For demonstration purposes
            // the timebase-rebase functionality has been included and choosing that mode copies the sample
            // and resets the time. If the user does not rebase the time we simply send the input sample
            // to the Sink Writer as-is.
            try
            {
                // Set status flags.
                outputSampleDataStruct.dwStatus = MFTOutputDataBufferFlags.None;
                // The output sample is the input sample. We get a new IUnknown for the Input
                // sample since we are going to release it below. The client will release this
                // new IUnknown
                outputSampleDataStruct.pSample = Marshal.GetIUnknownForObject(InputSample);
                // are we recording?
                if (workingSinkWriter != null)
                {
                    // we do everything in a lock
                    lock (sinkWriterLockObject)
                    {
                        // are we in timebase rebase mode?
                        if (wantTimebaseRebase == false)
                        {
                            // we are not. Just give the input sample to the Sink Writer which will
                            // write it out.
                            hr = workingSinkWriter.WriteSample(sinkWriterVideoStreamId, InputSample);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to WriteSample(a) failed. Err=" + hr.ToString());
                            }
                        }
                        else
                        {
                            // the timebase rebase option has been chosen. We need to create a copy of the input sample
                            // so we can adjust the time on it.
                            // Get the data buffer from the input sample. If the sample contains more than one buffer,
                            // this method copies the data from the original buffers into a new buffer, and replaces
                            // the original buffer list with the new buffer. The new buffer is returned in the inputMediaBuffer parameter.
                            // If the sample contains a single buffer, this method returns a pointer to the original buffer.
                            // In typical use, most samples do not contain multiple buffers.
                            hr = InputSample.ConvertToContiguousBuffer(out inputMediaBuffer);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to InputSample.ConvertToContiguousBuffer failed. Err=" + hr.ToString());
                            }
                            // get some other things from the input sample
                            hr = InputSample.GetSampleDuration(out long sampleDuration);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to InputSample.GetSampleDuration failed. Err=" + hr.ToString());
                            }
                            hr = InputSample.GetTotalLength(out int sampleSize);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to InputSample.GetTotalLength failed. Err=" + hr.ToString());
                            }
                            hr = InputSample.GetSampleTime(out long sampleTimeStamp);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to InputSample.GetSampleTime failed. Err=" + hr.ToString());
                            }
                            // get the attributes from the input sample
                            if (InputSample is IMFAttributes)
                            {
                                sampleAttributes = (InputSample as IMFAttributes);
                            }
                            else
                            {
                                sampleAttributes = null;
                            }
                            // we have all the information we need to create a new output sample
                            sinkWriterSample = WMFUtils.CreateMediaSampleFromBuffer(sampleTimeStamp, sampleDuration, inputMediaBuffer, sampleSize, sampleAttributes);
                            if (sinkWriterSample == null)
                            {
                                throw new Exception("OnProcessOutput, Error on call to CreateMediaSampleFromBuffer sinkWriterSample == null");
                            }
                            // we have a sample, if so is it the first non null one?
                            if (isFirstSample)
                            {
                                // yes it is set up our timestamp
                                firstSampleBaseTime = sampleTimeStamp;
                                isFirstSample       = false;
                            }
                            // rebase the time stamp
                            sampleTimeStamp -= firstSampleBaseTime;
                            hr = sinkWriterSample.SetSampleTime(sampleTimeStamp);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to InputSample.SetSampleTime failed. Err=" + hr.ToString());
                            }
                            // write the sample out
                            hr = workingSinkWriter.WriteSample(sinkWriterVideoStreamId, sinkWriterSample);
                            if (hr != HResult.S_OK)
                            {
                                throw new Exception("OnProcessOutput call to WriteSample(b) failed. Err=" + hr.ToString());
                            }
                        }
                    }
                }
            }
            finally
            {
                // clean up
                if (inputMediaBuffer != null)
                {
                    Marshal.ReleaseComObject(inputMediaBuffer);
                }
                if (sinkWriterSample != null)
                {
                    Marshal.ReleaseComObject(sinkWriterSample);
                }
                // Release the current input sample so we can get another one.
                // the act of setting it to null releases it because the property
                // is coded that way
                InputSample = null;
            }
            return(HResult.S_OK);
        }
예제 #8
0
    protected void UpdateInput()
    {
        bool            flag;
        bool            flag2;
        InventoryHolder inventoryHolder = this.inventoryHolder;

        PlayerClient.InputFunction(base.gameObject);
        if (inventoryHolder != null)
        {
            ItemModFlags modFlags = inventoryHolder.modFlags;
            flag  = (modFlags & ItemModFlags.Lamp) == ItemModFlags.Other;
            flag2 = (modFlags & ItemModFlags.Laser) == ItemModFlags.Other;
        }
        else
        {
            flag = flag2 = true;
        }
        InputSample sample = InputSample.Poll(flag, flag2);

        sample.info__crouchBlocked = this.crouch_was_blocked;
        bool flag3 = base.GetLocal <FallDamage>().GetLegInjury() > 0f;

        if (flag3)
        {
            sample.crouch = true;
            sample.jump   = false;
        }
        if (((sample.walk <= 0f) || (Mathf.Abs(sample.strafe) >= 0.05f)) || ((sample.attack2 || this._inventory.isCrafting) || flag3))
        {
            sample.sprint = false;
        }
        float num = 1f;

        if (this._inventory.isCrafting)
        {
            num *= 0.5f;
        }
        if (flag3)
        {
            num *= 0.5f;
        }
        InputSample.MovementScale = num;
        if (inventoryHolder != null)
        {
            object item = inventoryHolder.InvokeInputItemPreFrame(ref sample);
            this.ProcessInput(ref sample);
            inventoryHolder.InvokeInputItemPostFrame(item, ref sample);
        }
        else
        {
            this.ProcessInput(ref sample);
        }
        this.CheckBeltUsage();
        if (this.wasSprinting && !sample.sprint)
        {
            this.SprintingStopped();
        }
        else if (!this.wasSprinting && sample.sprint)
        {
            this.SprintingStarted();
        }
    }
예제 #9
0
    private void ProcessInput(ref InputSample sample)
    {
        bool    isGrounded;
        bool    isSliding;
        CCMotor ccmotor = base.ccmotor;

        if (ccmotor != null)
        {
            CCMotor.InputFrame frame;
            isGrounded = ccmotor.isGrounded;
            isSliding  = ccmotor.isSliding;
            if (!isGrounded && !isSliding)
            {
                sample.sprint = false;
                sample.crouch = false;
                sample.aim    = false;
                sample.info__crouchBlocked = false;
                if (!this.wasInAir)
                {
                    this.wasInAir       = true;
                    this.magnitudeAir   = ccmotor.input.moveDirection.magnitude;
                    this.midairStartPos = base.transform.position;
                }
                this.lastFrameVelocity = ccmotor.velocity;
            }
            else if (this.wasInAir)
            {
                this.wasInAir                = false;
                this.magnitudeAir            = 1f;
                this.landingSpeedPenaltyTime = 0f;
                if ((base.transform.position.y < this.midairStartPos.y) && (Mathf.Abs((float)(base.transform.position.y - this.midairStartPos.y)) > 2f))
                {
                    base.idMain.GetLocal <FallDamage>().SendFallImpact(this.lastFrameVelocity);
                }
                this.lastFrameVelocity = Vector3.zero;
                this.midairStartPos    = Vector3.zero;
            }
            bool flag3 = sample.crouch || sample.info__crouchBlocked;
            frame.jump            = sample.jump;
            frame.moveDirection.x = sample.strafe;
            frame.moveDirection.y = 0f;
            frame.moveDirection.z = sample.walk;
            frame.crouchSpeed     = !sample.crouch ? 1f : -1f;
            if (frame.moveDirection != Vector3.zero)
            {
                float num2;
                float num3;
                float magnitude = frame.moveDirection.magnitude;
                if (magnitude < 1f)
                {
                    frame.moveDirection = (Vector3)(frame.moveDirection / magnitude);
                    magnitude          *= magnitude;
                    frame.moveDirection = (Vector3)(frame.moveDirection * magnitude);
                }
                else if (magnitude > 1f)
                {
                    frame.moveDirection = (Vector3)(frame.moveDirection / magnitude);
                }
                if (InputSample.MovementScale < 1f)
                {
                    if (InputSample.MovementScale > 0f)
                    {
                        frame.moveDirection = (Vector3)(frame.moveDirection * InputSample.MovementScale);
                    }
                    else
                    {
                        frame.moveDirection = Vector3.zero;
                    }
                }
                Vector3 moveDirection = frame.moveDirection;
                moveDirection.x *= this.controlConfig.sprintScaleX;
                moveDirection.z *= this.controlConfig.sprintScaleY;
                if ((sample.sprint && !flag3) && !sample.aim)
                {
                    num2 = Time.deltaTime * this.sprintInMulTime;
                }
                else
                {
                    sample.sprint = false;
                    num2          = -Time.deltaTime;
                }
                frame.moveDirection += (Vector3)(moveDirection * this.controlConfig.curveSprintAddSpeedByTime.EvaluateClampedTime(ref this.sprintTime, num2));
                if (flag3)
                {
                    num3 = Time.deltaTime * this.crouchInMulTime;
                }
                else
                {
                    num3 = -Time.deltaTime;
                }
                frame.moveDirection = (Vector3)(frame.moveDirection * this.controlConfig.curveCrouchMulSpeedByTime.EvaluateClampedTime(ref this.crouchTime, num3));
                frame.moveDirection = base.transform.TransformDirection(frame.moveDirection);
                if (this.wasInAir)
                {
                    float a = frame.moveDirection.magnitude;
                    if (!Mathf.Approximately(a, this.magnitudeAir))
                    {
                        frame.moveDirection = (Vector3)(frame.moveDirection / a);
                        frame.moveDirection = (Vector3)(frame.moveDirection * this.magnitudeAir);
                    }
                }
                else
                {
                    frame.moveDirection = (Vector3)(frame.moveDirection * this.controlConfig.curveLandingSpeedPenalty.EvaluateClampedTime(ref this.landingSpeedPenaltyTime, Time.deltaTime));
                }
            }
            else
            {
                this.sprinting     = false;
                this.exitingSprint = false;
                this.sprintTime    = 0f;
                this.crouchTime    = !sample.crouch ? 0f : this.controlConfig.curveCrouchMulSpeedByTime.GetEndTime();
                this.magnitudeAir  = 1f;
            }
            if (DebugInput.GetKey(KeyCode.H))
            {
                frame.moveDirection = (Vector3)(frame.moveDirection * 100f);
            }
            ccmotor.input = frame;
            if (ccmotor.stepMode == CCMotor.StepMode.Elsewhere)
            {
                ccmotor.Step();
            }
        }
        else
        {
            isSliding  = false;
            isGrounded = true;
        }
        Character  idMain     = base.idMain;
        Crouchable crouchable = idMain.crouchable;

        if (idMain != null)
        {
            Angle2 eyesAngles = base.eyesAngles;
            eyesAngles.yaw   = Mathf.DeltaAngle(0f, base.eyesAngles.yaw + sample.yaw);
            eyesAngles.pitch = base.ClampPitch((float)(eyesAngles.pitch + sample.pitch));
            base.eyesAngles  = eyesAngles;
            ushort flags = idMain.stateFlags.flags;
            if (crouchable != null)
            {
                this.crouch_smoothing.AddSeconds((double)Time.deltaTime);
                crouchable.LocalPlayerUpdateCrouchState(ccmotor, ref sample.crouch, ref sample.info__crouchBlocked, ref this.crouch_smoothing);
            }
            int num6 = ((((((((((((!sample.aim ? 0 : 4) | (!sample.sprint ? 0 : 2)) | (!sample.attack ? 0 : 8)) | (!sample.attack2 ? 0 : 0x100)) | (!sample.crouch ? 0 : 1)) | (((sample.strafe == 0f) && (sample.walk == 0f)) ? 0 : 0x40)) | (!LockCursorManager.IsLocked() ? 0x80 : 0)) | (!isGrounded ? 0x10 : 0)) | (!isSliding ? 0 : 0x20)) | (!this.bleeding ? 0 : 0x200)) | (!sample.lamp ? 0 : 0x800)) | (!sample.laser ? 0 : 0x1000)) | (!sample.info__crouchBlocked ? 0 : 0x400);
            idMain.stateFlags = num6;
            if (flags != num6)
            {
                idMain.Signal_State_FlagsChanged(false);
            }
        }
        this.crouch_was_blocked = sample.info__crouchBlocked;
        if (sample.inventory)
        {
            RPOS.Toggle();
        }
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            RPOS.Hide();
        }
    }
예제 #10
0
        override protected HResult OnProcessOutput(ref MFTOutputDataBuffer pOutputSamples)
        {
            HResult hr = HResult.S_OK;
            MFError throwonhr;

            // Since we don't specify MFTOutputStreamInfoFlags.ProvidesSamples, this can't be null.
            if (pOutputSamples.pSample != IntPtr.Zero)
            {
                long hnsDuration;
                long hnsTime;

                IMFMediaBuffer pInput     = null;
                IMFMediaBuffer pOutput    = null;
                IMFSample      pOutSample = null;

                try
                {
                    // Get the data buffer from the input sample.  If the sample has
                    // multiple buffers, you might be able to get (slightly) better
                    // performance processing each buffer in turn rather than forcing
                    // a new, full-sized buffer to get created.
                    throwonhr = InputSample.ConvertToContiguousBuffer(out pInput);

                    // Turn pointer to interface
                    pOutSample = Marshal.GetUniqueObjectForIUnknown(pOutputSamples.pSample) as IMFSample;

                    // Get the output buffer.
                    throwonhr = pOutSample.ConvertToContiguousBuffer(out pOutput);

                    OnProcessOutput(pInput, pOutput);

                    // Set status flags.
                    pOutputSamples.dwStatus = MFTOutputDataBufferFlags.None;

                    // Copy the duration and time stamp from the input sample,
                    // if present.

                    hr = InputSample.GetSampleDuration(out hnsDuration);
                    if (Succeeded(hr))
                    {
                        throwonhr = pOutSample.SetSampleDuration(hnsDuration);
                    }

                    hr = InputSample.GetSampleTime(out hnsTime);
                    if (Succeeded(hr))
                    {
                        throwonhr = pOutSample.SetSampleTime(hnsTime);
                    }
                }
                finally
                {
                    SafeRelease(pInput);
                    SafeRelease(pOutput);
                    SafeRelease(pOutSample);

                    // Release the current input sample so we can get another one.
                    InputSample = null;
                }
            }
            else
            {
                return(HResult.E_INVALIDARG);
            }

            return(HResult.S_OK);
        }
예제 #11
0
        /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
        /// <summary>
        /// This is the routine that performs the transform. Assumes InputSample is set.
        ///
        /// An override of the abstract version in TantaMFTBase_Sync.
        /// </summary>
        /// <param name="outputSampleDataStruct">The structure to populate with output data.</param>
        /// <returns>S_Ok unless error.</returns>
        /// <history>
        ///    01 Nov 18  Cynic - Ported In
        /// </history>
        protected override HResult OnProcessOutput(ref MFTOutputDataBuffer outputSampleDataStruct)
        {
            long           hnsDuration;
            long           hnsTime;
            HResult        hr = HResult.S_OK;
            IMFMediaBuffer inputMediaBuffer  = null;
            IMFMediaBuffer outputMediaBuffer = null;
            IMFSample      outputSample      = null;

            // Since we don't specify MFTOutputStreamInfoFlags.ProvidesSamples, this can't be null.
            // we expect the caller to have allocated the memory for this
            if (outputSampleDataStruct.pSample == IntPtr.Zero)
            {
                return(HResult.E_INVALIDARG);
            }

            try
            {
                // Get the data buffer from the input sample. If the sample contains more than one buffer,
                // this method copies the data from the original buffers into a new buffer, and replaces
                // the original buffer list with the new buffer. The new buffer is returned in the inputMediaBuffer parameter.
                // If the sample contains a single buffer, this method returns a pointer to the original buffer.
                // In typical use, most samples do not contain multiple buffers.
                hr = InputSample.ConvertToContiguousBuffer(out inputMediaBuffer);
                if (hr != HResult.S_OK)
                {
                    throw new Exception("OnProcessOutput call to InputSample.ConvertToContiguousBuffer failed. Err=" + hr.ToString());
                }

                // Turn pointer into an interface. The GetUniqueObjectForIUnknown method ensures that we
                // receive a unique Runtime Callable Wrapper, because it does not match an IUnknown pointer
                // to an existing object. Use this method when you have to create a unique RCW that is not
                // impacted by other code that calls the ReleaseComObject method.
                outputSample = Marshal.GetUniqueObjectForIUnknown(outputSampleDataStruct.pSample) as IMFSample;
                if (outputSample == null)
                {
                    throw new Exception("OnProcessOutput call to GetUniqueObjectForIUnknown failed. outputSample ==  null");
                }

                // Now get the output buffer. A media sample contains zero or more buffers. Each buffer manages a block of
                // memory, and is represented by the IMFMediaBuffer interface. A sample can have multiple buffers.
                // The buffers are kept in an ordered list and accessed by index value. This call gets us a single
                // pointer to a single contigous buffer which is much more useful.
                hr = outputSample.ConvertToContiguousBuffer(out outputMediaBuffer);
                if (hr != HResult.S_OK)
                {
                    throw new Exception("OnProcessOutput call to InputSample.ConvertToContiguousBuffer failed. Err=" + hr.ToString());
                }

                // now that we have an input and output buffer, do the work to convert them to grayscale.
                // Writing into outputMediaBuffer will write to the approprate location in the outputSample
                // since we took care to Marshal it that way
                ConvertMediaBufferToGrayscale(inputMediaBuffer, outputMediaBuffer);

                // Set status flags.
                outputSampleDataStruct.dwStatus = MFTOutputDataBufferFlags.None;

                // Copy the duration from the input sample, if present. The
                // Media Session needs these in order to keep everything sync'ed
                hr = InputSample.GetSampleDuration(out hnsDuration);
                if (hr == HResult.S_OK)
                {
                    hr = outputSample.SetSampleDuration(hnsDuration);
                    if (hr != HResult.S_OK)
                    {
                        throw new Exception("OnProcessOutput call to OutSample.SetSampleDuration failed. Err=" + hr.ToString());
                    }
                }

                // Copy the time stamp from the input sample, if present.
                hr = InputSample.GetSampleTime(out hnsTime);
                if (hr == HResult.S_OK)
                {
                    hr = outputSample.SetSampleTime(hnsTime);
                    if (hr != HResult.S_OK)
                    {
                        throw new Exception("OnProcessOutput call to OutSample.SetSampleTime failed. Err=" + hr.ToString());
                    }
                }
            }
            finally
            {
                // clean up
                SafeRelease(inputMediaBuffer);
                SafeRelease(outputMediaBuffer);
                SafeRelease(outputSample);

                // Release the current input sample so we can get another one.
                // the act of setting it to null releases it because the property
                // is coded that way
                InputSample = null;
            }

            return(HResult.S_OK);
        }