Sascha-L / WPF-MediaKit

Microsoft Public License
361 stars 114 forks source link

EnableSampleGrabbing does not seem to work. #6

Open dellis1972 opened 9 years ago

dellis1972 commented 9 years ago

@Sascha-L As the title says, enabling EnableSampleGrabbing does not seem to result in the NewVideoSample event being raised at all. The code does seem to setup the SampleGrabber filter but as far as I can see the ISampleGrabberCB.SampleCB never gets hit. This is with a basic VideoCaptureElement like so

        <Controls:VideoCaptureElement x:Name="videoCapElement"
                                 DesiredPixelWidth="640"
                                  DesiredPixelHeight="480"
                                  Width="300" Height="240"
                                  Stretch="Fill"
                                      EnableSampleGrabbing="True"
                                      UseYuv="False"
                                      NewVideoSample="videoCapElement_NewVideoSample"
                                 FPS="30" />

The video source /name is setup in code behind and play is called on a button click so its not a complicate app. The Video from the webcam displays just fine in the form.

Any ideas?

dellis1972 commented 8 years ago

Looks like it works on some cameras but not on others :/

AenBleidd commented 6 years ago

I have the same problem Is there some workaround? How can I get every captured frame?

MatriXiao88 commented 6 years ago
  1. Your camera must has the still pin, check it out with GraphStudioNext;
  2. You should add some code that setup some parameters of still pin.
  3. Sometimes you should add a decoder filter between still pin and input pin of render filter, and connect them all.

        //setup parameters of still pin
        private void ConfigStillPinParameter(IPin pStillPin)
        {
            AMMediaType mediaType;
            IAMStreamConfig streamConfig = pStillPin as IAMStreamConfig;
            var hr = streamConfig.GetFormat(out mediaType);
            DsError.ThrowExceptionForHR(hr);
    
            try
            {
                var headerInfo = new VideoInfoHeader();
                Marshal.PtrToStructure(mediaType.formatPtr, headerInfo);
    
                if (SampleImageWidth > 0)
                {
                    headerInfo.BmiHeader.Width = SampleImageWidth;
                }
                if (SampleImageHeight > 0)
                {
                    headerInfo.BmiHeader.Height = SampleImageHeight;
                }
                if (SampleImageBitCount > 0)
                {
                    headerInfo.BmiHeader.BitCount = SampleImageBitCount;
                }
                Marshal.StructureToPtr(headerInfo, mediaType.formatPtr, false);
    
                hr = streamConfig.SetFormat(mediaType);
                DsError.ThrowExceptionForHR(hr);
            }
            finally
            {
                DsUtils.FreeAMMediaType(mediaType);
            }
        }
bbetter commented 6 years ago

@dellis1972 or @Rhuaer , have you ever managed to make this done for the type of cameras where it's not working out of box

@Rhuaer The code you've suggested, seems really out of context for me, i'm pretty new to all the c# wpf staff, but where can I get this IPin class , is it sort of some kind of library, I'd really appreciate if you can answer me here in details or just email to me. Thank you in advance

xmedeko commented 6 years ago

@bbetter This IPin etc. stuff is related to DirectX - DirectShow Windows API.

bbetter commented 6 years ago

@xmedeko but how can i get this IPin pStillPin to pass it to this method

MatriXiao88 commented 6 years ago

@bbetter Class DsFindPin is useful. Here is what you want.

m_pStillPin = DsFindPin.ByCategory(m_captureDevice, PinCategory.Still, 0);

You should read some DirectShow.NET sampe code, if you want to know more about directShow in C#

bbetter commented 6 years ago

thank you for your response, m_captureDevicce is of param type IBaseFilter and the devicce i'm getting from mediakit videocaptureelement is DsDevice? I'd really appreciate if you can just send me some sort of sample app, whihch operates with MediaKit VideoCaptureElement as well as with DirectShow staff. @Rhuaer @xmedeko

yexcoffier commented 6 years ago

I have the same issue with a Surface Pro 4, front and back camera (but a logitech webcam on usb works perfectly). As @bbetter said, is there any sample that we could use ? I have absolutely no knowledge about DirectShow stuff and there is too many things I don't understand in @Rhuaer answer that it makes it difficult for me to use without spending I don't know how long time in DirectShow documentations. @bbetter @AenBleidd Have you been able to solve your problem ?

Thank you all in advance.

MatriXiao88 commented 6 years ago

@bbetter @euhics Here is the the sample code, Directshow .NET, you can read the code in \DirectShowSamples-2010-February\Samples\Capture\DxSnap.

Hope that is useful for you.

yexcoffier commented 6 years ago

Thank you @Rhuaer, that did help me have a much better understanding of what's going on. The Surface Pro has no pinStill.. In DxSnap they try to get still pin and if not found they use the preview pin. The Surface has a preview pin. So I tried to connect it to the sample grabber (or I think I did, DirectShow is still so new for me, I don't understand half of what I'm doing) but then I don't even have an image anymore. Am I doing it wrong or is just because we cannot use anything else except this still pin ?

Thank you again for your help.

yexcoffier commented 6 years ago

I finally gave up and ended up doing a render of my view with a RenderTargetBitmap. That is actually quite efficient for my case as I don't need a good quality render.

bbetter commented 6 years ago

I've been a bit off the task for few weeks, but still need to finish this one, any chance @euhics , that you have an example with RenderTargetBitmap?

yexcoffier commented 6 years ago

@bbetter

<Viewbox Width="400" Stretch="Uniform">
    <mediakitControls:VideoCaptureElement x:Name="Webcam"
        Width="640"
        HorizontalAlignment="Stretch"
        VerticalAlignment="Stretch"
        EnableSampleGrabbing="True"
        FPS="30"
        LoadedBehavior="Play"
        SizeChanged="Webcam_SizeChanged"
        UnloadedBehavior="Pause"
        VideoCaptureSource="{Binding SelectedVideoDevice, Mode=OneWay}" />
</Viewbox>

I put the control in a viewbox, so the render target bitmap will take the intended quality (in my case 640 x 480 if it's a 4:3 ratio camera) but will be shown with a 400 with on wpf. Weirdly, I need to set EnableSampleGrabbing to true, otherwise it crash on my webcam that has the still pin

Then the code behind :

private RenderTargetBitmap _rtbitmap;
...
private void Webcam_SizeChanged(object sender, SizeChangedEventArgs e)
{
    var size = args.EventArgs.NewSize;
    if (size.Width > 0 && size.Height > 0)
        _rtbitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96, 96, PixelFormats.Pbgra32);
}
...
// I put this code in a timer that execute every 100ms on the dispatcher thread. I advise using a DispatcherTimer
{
    _rtbitmap.Render(Webcam);
// In my case I need a System.Drawing.Bitmap so I continue with this :
    MemoryStream stream = new MemoryStream();
    BitmapEncoder encoder = new BmpBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(_rtbitmap));
    encoder.Save(stream);

    var bmp = new Bitmap(stream);
... // some treatment, I use this to decode a barcode
    bmp.Dispose();
    stream.Dispose();
}
MatriXiao88 commented 6 years ago

You can create D3DImage2 inherited from D3DImage, and replace D3DImage with D3DImage2 in D3DRender.

    public class D3DImage2 : D3DImage
    {
        public BitmapSource NewBitmapSource()
        {
            return CopyBackBuffer();
        }
    }

Then you can use NewBitmapSource in your project.

bbetter commented 6 years ago

@euhics , Thank you very much.

xmedeko commented 6 years ago

Guys, do not know much about the problem, but any chance to melt it into a PR? Or at least some piece of it?