Capture still images with UWP MediaCapture (PIN_CATEGORY_STILL) - c#

An alternative title could be: What happened to PIN_CATEGORY_STILL?
I am currently comparing images that were captured using DirectShow and PIN_CATEGORY_STILL with images that were captured using UWP MediaCapture.
On the device I am testing/playing around with DirectShow and MediaCapture, DirectShow detects a PIN_CATEGORY_STILL but I am not able to initialize an instance of MediaCapture with anything other than PhotoCaptureSource.VideoPreview.
MediaCaptureInitializationSettings settings = new()
{
VideoDeviceId = someDeviceId,
PhotoCaptureSource = PhotoCaptureSource.Photo
};
MediaCapture capture = new();
// this throws an exception
// "The capture source does not have an independent photo stream."
await capture.InitializeAsync(settings);
At this point I'm not even sure if PhotoCaptureSource.Photo is meant to be used as an equivalent to PIN_CATEGORY_STILL.
Images captured with PIN_CATEGORY_STILL are way brighter in a dark environment and have a much better quality (in file size and resolution) (which is clear to me, since I am using PhotoCaptureSource.VideoPreview for MediaCapture).
Considering this resource Win32 and COM for UWP apps, it seems like UWP MediaCapture does not use DirectShow underneath but MediaFoundation (which is meant to be a successor for DirectShow).
This article led me to this StackOverflow question Media Foundation is incorrectly marking still image capture stream descriptors as video capture, which basically states that MediaFoundation has no PIN_CATEGORY_STILL but returns 1 FPS as video capability for such devices (or profiles).
Since I am not directly using MediaFoundation nor C++, I tried testing this by querying GetAvailableMediaStreamProperties:
private void Foo()
{
var videoPreviewProperties = GetEncodingProperties(MediaStreamType.VideoRecord);
var photoProperties = GetEncodingProperties(MediaStreamType.Photo);
var videoRecordProperties = GetEncodingProperties(MediaStreamType.VideoPreview);
}
private List<VideoEncodingProperties> GetEncodingProperties(MediaStreamType streamType)
{
// MediaCapture was previously initialized with PhotoCaptureSource.VideoPreview
return MediaCapture.VideoDeviceController
.GetAvailableMediaStreamProperties(streamType)
.OfType<VideoEncodingProperties>()
.ToList();
}
None of these returns a VideoEncodingProperties with only 1 FPS.
To test MediaCapture any further I tried some of the sample applications from here UWP sample. I tried CameraAdvancedCapture, CameraOpenCV and CameraManualControls, but the results were not nearly as good as good old PIN_CATEGORY_STILL.
What happened to PIN_CATEGORY_STILL?
Is there any way to capture images without DirectShow/PIN_CATEGORY_STILL and still keeping this level of quality?
Any enlightenment is much appricated.

Related

How can I fix overexposed high resolution photos using Media Foundation?

I'm using Media Foundation in my WPF application to implement functionality that is similar to the Windows Camera App: show a live webcam feed and take a high resolution image when pressing a button. While the Windows Camera App always takes a correctly exposed snapshot, the snapshot taken by Media Foundation is often overexposed.
The application is only used by devices that have built-in high resolutions for Photos (eg Surface Go 2, Surface 7 Pro, ...). A specific image stream is used to take the snapshot instead of reading one frame of the video stream.
I have already tried changing the IAMCameraControl and IAMVideoProcAmp properties like Exposure or Brightness before taking a snapshot but the photo was always overexposed.
Are there any additional settings or approaches for fixing overexposure when taking a high resolution snapshot (eg. the same way the Windows Camera App works)?
Overexposed snapshot
High resolutions for photos
Code to take the snapshot
IMFCaptureEngineClassFactory captureEngineClassFactory = null;
IMFCaptureEngine captureEngine = null;
IMFCapturePhotoSink capturePhotoSink = null;
IMFMediaType photoMediaType = null;
try
{
// Create a CaptureEngineClassFactory
captureEngineClassFactory = mff.GetCaptureEngineClassFactory();
// Create a CaptureEngine
captureEngine = mff.GetCaptureEngine(captureEngineClassFactory);
// Initialize the CaptureEngine
MFCaptureEngineOnEventCallback captureEngineOnEventCallback = MFCaptureEngineOnEventCallback.GetInstance();
mff.Initialize(captureEngine, captureEngineOnEventCallback, mediaSource);
captureEngineOnEventCallback.WaitUntilInitialized();
// Create a Photo Sink
capturePhotoSink = mff.CreateCapturePhotoSink(captureEngine);
// Create a Photo Media Type
photoMediaType = CreatePhotoMediaType(mediaType);
// Remove all streams
mff.RemoveAllStreams(capturePhotoSink);
// Add Stream
int sinkStreamIndex = mff.AddStream(capturePhotoSink, streamIndex, photoMediaType);
// Set output filename
mff.SetSampleCallback(capturePhotoSink, this);
// Take photo
mff.TakePhoto(captureEngine);
captureEngineOnEventCallback.WaitUntilDone();
// imageSource should be filled in after executing TakePhoto
return image;
}
finally
{
TryRelease(captureEngineClassFactory);
TryRelease(captureEngine);
TryRelease(capturePhotoSink);
TryRelease(photoMediaType);
}

Playing an HDHomeRun stream on a Tizen.NET Xamarin App

I'm trying to play a HDHomeRun Connect Video source from a url in the following format: http://x.x.x.x:xxxx/auto/v4.1. This video source is an MPEG2 video encoding and AC3 audio encoding.
I've tried using the Samsung Tizen.TV .NET sample with the following source but the video never plays.
_player = new Tizen.Multimedia.Player();
var mediaSource = new Multimedia.MediaUriSource(uri);
_player.SetSource(mediaSource);
var display = new Multimedia.Display(Window.Instance);
_player.Display = display;
await _player.PrepareAsync();
The player state gets stuck in preparing, and the await _player.PrepareAsync() call never finishes. It is worth noting that I'm using the Tizen Samsung TV Emulator. Do I need to transcode the stream from the HDHomeRun to be playable? Are there any other measures I might be missing for the Video to play?
Ultimately, the Display property of the player wasn't being set correctly. The property that worked for me (found from investigating the JuvoPlayer code was this:
var display = new Multimedia.Display(((FormsApplication)Forms.Context).MainWindow);
_player.Display = display;
When you are to develop a Tizen .NET application, please be aware of which UI framework your project is targetted for among 3 different types: Xamarin.Forms, (pure) ElmSharp, and Tizen.NUI.
Unless your project is based on the Tizen.NUI framework, you shouldn't use Tizen.NUI.Window.Instance and types in Tizen.NUI namespace in any case. Instead, you will have to use types of ElmSharp or Xamarin.Forms.Platform.Tizen namespace for platform-specific code in your application.
Since the internal implementation of Xamarin.Forms for Tizen is based on ElmSharp, FormsApplication.MainWindow will return a ElmSharp.Window instance which can be used to instantiate a Tizen.Multimedia.Display object. That's why the code in your answer worked.

How to display 2 web camera preview in UWP?

Hello I displayed 1 webcam preview in UWP and that was a success.
But now I want to use 2 camera's preview on my program or be able to choose between the two cameras while connected 2 cameras on computer.
When I run 1 webcam preview, I referred to documentation on using MediaCapture and it was good.
But now I don't know how to display 2 camera previews or select a one between cameras.
Is it impossible?
Yes, it is possible :-) . The MediaCapture class takes the default camera when you call the InitializeAsync method without parameters, but there is another overload that allows you to specify the device ID.
The documentation shows how to discover video capture devices:
DeviceInformationCollection devices =
await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
Now you can initialize multiple MediaCapture instances like this:
foreach ( var device in devices )
{
var mediaInitSettings =
new MediaCaptureInitializationSettings { VideoDeviceId = device.Id };
MediaCapture mediaCapture = new MediaCapture();
mediaCapture.InitializeAsync(mediaInitSettings);
//do something with the media capture
}
Naturally, when you want to display multiple previews, you will need to have multiple CaptureElements, each set to the specific MediaCapture instance you want.
However this approach is quite simplified. To make sure the concurrent capture and preview is supported, you must first ensure to query only cameras that support device profile using MediaCapture.IsVideoProfileSupported method as shown in the documentation and then also check find a concurrency-enabled profile common for both cameras - MediaCapture.FindConcurrentProfiles, see docs. Only then you can safely create the two previews and know the app will not crash.

MediaPlayerLauncher on WP7 - how to resume previously playing media?

I'm using a MediaPlayerLauncher to show movietrailers in my WP7 application, like this:
MediaPlayerLauncher mpl = new MediaPlayerLauncher();
mpl.Media = new Uri(trailerUrl, UriKind.Absolute);
mpl.Controls = MediaPlaybackControls.All;
mpl.Show();
This works just fine, except one thing: if the user is already listening to music in the background, and launch a trailer, the music is not resumed after the trailer is done playing (or if the user closes the video).
Does anyone know how i can resume the previously playing music/media, if even possible?
Local media playing through XNA or a 'background audio agent'?
When you play media in WP7 / WP8, the OS audio context is taken, and the original context is lost. If the audio was launched from an external application, then you cannot resume at all. If the previous media was launched from within your application, then you could store the meta-data and re-play once your trailer is finished. The media would, of course, then begin playing from the start, rather than where the user left off. Unfortunately XNA does not allow you to seek within a given piece of media; however you can seek within an 'audio agent' instance of 'BackgroundAudioPlayer' by setting player.Position. It's also worth looking at the MediaHistory API:
var nowPlaying = Microsoft.Devices.MediaHistory.Instance.NowPlaying;
Figured it out. Calling MediaPlayer.Resume() right after show() solves the issue:
mpl.Media = new Uri(trailerurl, UriKind.Absolute);
mpl.Controls = MediaPlaybackControls.All;
mpl.Show();
MediaPlayer.Resume();
However, i would still like to know how to resume radio and spotify!

Webcams, C#, and a reliable wrapper

I'm in dire need of a replacement for a wrapper being used in a C# application. Basically, what we need to do is attach a webcam feed to one of two picture boxes. This will be used to take still images at the push of a button, which may detach the camera feed and attach the still image to that picture box, then reattach the camera feed later. We had previously found some free code to utilize with a CaptureDevice.cs file and Pinvoke.dll to tie it into avicap32.dll. Unfortunately, this seems to have random, intermittent errors that cannot be reliably reproduced. It's just too flaky. At some random point, one of those picture boxes may go black and won't show the feed until the picture is taken, at which point the proper picture is attached to the picture box. Then, even if there's only one webcam attached, it'll keep prompting for the webcam to be selected, something it wouldn't do otherwise.
Quite frankly, I'm surprised and dismayed that Microsoft hasn't included anything in .NET to cover webcam video feeds. I'm looking for something reliable and relatively simple to implement to replace this buggy webcam system.
It is time to use MediaCapture object. Use this sample for MS Windows 10 https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/CameraStarterKit/
And read this article as well http://www.codepool.biz/csharp-camera-api-video-frame.html
May I suggest
http://www.emgu.com/wiki/index.php/Main_Page
I have used OpenCV in many C++ libraries and it seems to work very well for webcams from other things I've tried. Emgu is just a C# wrapper for OpenCV.
Here is a sample project to try it out on. It's very basic and simple, but should work right away.
http://dl.dropbox.com/u/18919663/vs%20samples/OpenCVCSharpTest.zip (just uploaded)
Sample:
using Emgu.CV;
using Emgu.CV.Structure;
...
public partial class Form1 : Form
{
public Capture cvWebCam;
public Form1()
{
InitializeComponent();
try
{
cvWebCam = new Capture();
timer1.Start();
}
catch
{
Console.WriteLine("Default camera not found or could not start");
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (cvWebCam != null)
using (Emgu.CV.Image<Bgr, byte> frame = cvWebCam.QueryFrame())
{
pictureBox1.BackgroundImage = frame.ToBitmap();
}
}
}
Try DirectShow.net- it's a free wrapper library to access DirectShow functionality from .NET:
http://directshownet.sourceforge.net
Its code samples contain a sample app for capturing video from webcams, too:
http://sourceforge.net/projects/directshownet/files/DirectShowSamples/

Categories