Aracılığıyla paylaş


MediaFrameReader ile medya çerçevelerini işleme

Bu makalede renk, derinlik ve kızılötesi kameralar, ses cihazları ve hatta iskelet izleme çerçeveleri üretenler gibi özel çerçeve kaynakları dahil olmak üzere bir veya daha fazla kullanılabilir kaynaktan medya çerçeveleri almak için MediaCapture ile MediaFrameReader nasıl kullanılacağı gösterilmektedir. Bu özellik, artırılmış gerçeklik ve derinlik algılayan kamera uygulamaları gibi medya çerçevelerinin gerçek zamanlı olarak işlenmesini gerçekleştiren uygulamalar tarafından kullanılmak üzere tasarlanmıştır.

Tipik bir fotoğraf uygulaması gibi yalnızca video veya fotoğraf çekmek istiyorsanız, MediaCapture tarafından desteklenen diğer yakalama tekniklerinden birini kullanmak isteyebilirsiniz. Kullanılabilir medya yakalama tekniklerinin ve bunların nasıl kullanılacağını gösteren makalelerin listesi için bkz. Kamera.

Uyarı

Bu makalede açıklanan özellikler yalnızca Windows 10, sürüm 1607'den itibaren kullanılabilir.

Uyarı

Renk, derinlik ve kızılötesi kameralar gibi farklı çerçeve kaynaklarından kareleri görüntülemek için MediaFrameReader'ın kullanılmasını gösteren bir Evrensel Windows uygulaması örneği vardır. Daha fazla bilgi için bkz . Kamera çerçeveleri örneği.

Uyarı

Windows 10, sürüm 1803'te MediaFrameReader'ı ses verileriyle kullanmaya yönelik yeni bir API kümesi kullanıma sunulmuştur. Daha fazla bilgi için bkz . MediaFrameReader ile ses çerçevelerini işleme.

Çerçeve kaynaklarını ve çerçeve kaynak gruplarını seçme

Medya çerçevelerini işleyen birçok uygulamanın, cihazın rengi ve derinlik kameraları gibi birden çok kaynaktan aynı anda kare alması gerekir. MediaFrameSourceGroup nesnesi, aynı anda kullanılabilecek bir medya çerçevesi kaynakları kümesini temsil eder. Geçerli cihaz tarafından desteklenen tüm çerçeve kaynak gruplarının listesini almak için MediaFrameSourceGroup.FindAllAsync statik yöntemini çağırın.

var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();

Ayrıca DeviceInformation.CreateWatcher kullanarak bir DeviceWatcher oluşturabilir ve MediaFrameSourceGroup.GetDeviceSelector'dan döndürülen değeri kullanarak, örneğin bir dış kamera takıldığında cihazdaki kullanılabilir çerçeve kaynak grupları değiştiğinde bildirim almak için oluşturabilirsiniz. Daha fazla bilgi için bkz: cihazları listeleme.

MediaFrameSourceGroup, gruba dahil edilen çerçeve kaynaklarını açıklayan bir MediaFrameSourceInfo nesneleri koleksiyonuna sahiptir. Cihazda kullanılabilen çerçeve kaynak gruplarını aldıktan sonra, ilgilendiğiniz çerçeve kaynaklarını kullanıma sunan grubu seçebilirsiniz.

Aşağıdaki örnekte, çerçeve kaynak grubu seçmenin en basit yolu gösterilmektedir. Bu kod yalnızca tüm kullanılabilir grupları döngüye alır ve ardından SourceInfos koleksiyonundaki her öğe üzerinde döngü gerçekleştirir. Her MediaFrameSourceInfo , aradığımız özellikleri desteklenip desteklemediğini görmek için denetlenmektedir. Bu durumda, MediaStreamType özelliği, VideoPreviewdeğeri için denetlenerek cihazın bir video önizleme akışı sağladığı anlamına gelir ve SourceKind özelliği Renkdeğeri için denetlenerek kaynağın renk çerçeveleri sağladığını gösterir.

var frameSourceGroups = await MediaFrameSourceGroup.FindAllAsync();

MediaFrameSourceGroup selectedGroup = null;
MediaFrameSourceInfo colorSourceInfo = null;

foreach (var sourceGroup in frameSourceGroups)
{
    foreach (var sourceInfo in sourceGroup.SourceInfos)
    {
        if (sourceInfo.MediaStreamType == MediaStreamType.VideoPreview
            && sourceInfo.SourceKind == MediaFrameSourceKind.Color)
        {
            colorSourceInfo = sourceInfo;
            break;
        }
    }
    if (colorSourceInfo != null)
    {
        selectedGroup = sourceGroup;
        break;
    }
}

İstenen çerçeve kaynak grubunu ve çerçeve kaynaklarını tanımlamanın bu yöntemi basit durumlarda işe yarar, ancak daha karmaşık ölçütlere göre çerçeve kaynaklarını seçmek istiyorsanız, hızlı bir şekilde hantal hale gelebilir. Başka bir yöntem, seçimi yapmak için Linq söz dizimini ve anonim nesneleri kullanmaktır. Aşağıdaki örnek, frameSourceGroups listesindeki MediaFrameSourceGroup nesnelerini iki alanı olan anonim bir nesneye dönüştürmek için Uzantı seçme yöntemini kullanır: grubun kendisini temsil eden sourceGroup ve gruptaki renk çerçevesi kaynağını temsil eden colorSourceInfo. colorSourceInfo alanı, sağlanan koşulun true olarak çözümlendiği ilk nesneyi seçen FirstOrDefaultsonucunun sonucu olarak ayarlanır. Bu durumda, akış türü VideoPreviewise, kaynak türü Colorve kamera cihazın ön panelindeyse, ifade doğrudur.

Yukarıda açıklanan sorgudan döndürülen anonim nesneler listesinden , Where uzantısı yöntemi yalnızca colorSourceInfo alanının null olmadığı nesneleri seçmek için kullanılır. Son olarak, listedeki ilk öğeyi seçmek için FirstOrDefault çağrılır.

Artık seçili MediaFrameSourceGroup ve renkli kamerayı temsil eden MediaFrameSourceInfo nesnesine başvurular almak için seçili nesnenin alanlarını kullanabilirsiniz. Bunlar daha sonra MediaCapture nesnesini başlatmak ve seçili kaynak için bir MediaFrameReader oluşturmak için kullanılacaktır. Son olarak, kaynak grubun null olup olmadığını, yani geçerli cihazın istediğiniz yakalama kaynaklarının olmadığını test etmelisiniz.

var selectedGroupObjects = frameSourceGroups.Select(group =>
   new
   {
       sourceGroup = group,
       colorSourceInfo = group.SourceInfos.FirstOrDefault((sourceInfo) =>
       {
           // On Xbox/Kinect, omit the MediaStreamType and EnclosureLocation tests
           return sourceInfo.MediaStreamType == MediaStreamType.VideoPreview
           && sourceInfo.SourceKind == MediaFrameSourceKind.Color
           && sourceInfo.DeviceInformation?.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front;
       })

   }).Where(t => t.colorSourceInfo != null)
   .FirstOrDefault();

MediaFrameSourceGroup selectedGroup = selectedGroupObjects?.sourceGroup;
MediaFrameSourceInfo colorSourceInfo = selectedGroupObjects?.colorSourceInfo;

if (selectedGroup == null)
{
    return;
}

Aşağıdaki örnek, renk, derinlik ve kızılötesi kameralar içeren bir kaynak grubu seçmek için yukarıda açıklandığı gibi benzer bir teknik kullanır.

var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var eligibleGroups = allGroups.Select(g => new
{
    Group = g,

    // For each source kind, find the source which offers that kind of media frame,
    // or null if there is no such source.
    SourceInfos = new MediaFrameSourceInfo[]
    {
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Color),
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Depth),
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Infrared),
    }
}).Where(g => g.SourceInfos.Any(info => info != null)).ToList();

if (eligibleGroups.Count == 0)
{
    System.Diagnostics.Debug.WriteLine("No source group with color, depth or infrared found.");
    return;
}

var selectedGroupIndex = 0; // Select the first eligible group
MediaFrameSourceGroup selectedGroup = eligibleGroups[selectedGroupIndex].Group;
MediaFrameSourceInfo colorSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[0];
MediaFrameSourceInfo infraredSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[1];
MediaFrameSourceInfo depthSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[2];

Uyarı

Windows 10, sürüm 1803'ten başlayarak, mediaCaptureVideoProfile sınıfını kullanarak istenen özelliklere sahip bir medya çerçevesi kaynağı seçebilirsiniz. Daha fazla bilgi için, bu makalenin ilerleyen bölümlerinde yer alan çerçeve kaynağı seçmek için video profillerini kullanma bölümüne bakın.

Seçili çerçeve kaynak grubunu kullanmak için MediaCapture nesnesini başlatın

Sonraki adım, önceki adımda seçtiğiniz çerçeve kaynak grubunu kullanmak için MediaCapture nesnesini başlatmaktır. Oluşturucuyu çağırarak MediaCapture nesnesinin bir örneğini oluşturun. Ardından, MediaCapture nesnesini başlatmak için kullanılacak bir MediaCaptureInitializationSettings nesnesi oluşturun. Bu örnekte aşağıdaki ayarlar kullanılır:

  • SourceGroup - Bu, sisteme çerçeveleri almak için hangi kaynak grubunu kullanacağınızı bildirir. Kaynak grubunun aynı anda kullanılabilecek bir medya çerçevesi kaynakları kümesi tanımladığını unutmayın.
  • SharingMode - Bu, sisteme yakalama kaynağı cihazları üzerinde özel denetime ihtiyacınız olup olmadığını bildirir. Bunu ExclusiveControl olarak ayarlarsanız, yakalama cihazının ayarlarını,ürettiği çerçevelerin biçimi gibi değiştirebilirsiniz, ancak başka bir uygulama zaten özel denetime sahipse, medya yakalama cihazını başlatmaya çalıştığında uygulamanızın başarısız olacağı anlamına gelir. Bunu SharedReadOnly olarak ayarlarsanız, başka bir uygulama tarafından kullanılıyor olsalar bile çerçeve kaynaklarından çerçeve alabilirsiniz, ancak cihazların ayarlarını değiştiremezsiniz.
  • MemoryPreference - Eğer CPUbelirtirseniz, sistem çerçeveler geldiğinde, bunların SoftwareBitmap nesneleri olarak mevcut olmasını garanti eden CPU belleğini kullanır. Otomatik'i belirtirseniz, sistem çerçeveleri depolamak için en uygun bellek konumunu dinamik olarak seçer. Sistem GPU belleği kullanmayı seçerse, medya çerçeveleri SoftwareBitmap olarak değil IDirect3DSurface nesnesi olarak gelir.
  • StreamingCaptureMode - Sesin akışa alınmasına gerek olmadığını belirtmek için bunu Video olarak ayarlayın.

InitializeAsync çağırarak MediaCapture istediğiniz ayarlarla başlatın. Başlatmanın başarısız olması durumunda, bunu bir try bloğu içinde çağırmayı unutmayın.

m_mediaCapture = new MediaCapture();

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = selectedGroup,
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
    StreamingCaptureMode = StreamingCaptureMode.Video
};
try
{
    await m_mediaCapture.InitializeAsync(settings);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine("MediaCapture initialization failed: " + ex.Message);
    return;
}

Çerçeve kaynağı için tercih edilen biçimi ayarlama

Çerçeve kaynağı için tercih edilen biçimi ayarlamak için, kaynağı temsil eden bir MediaFrameSource nesnesi almanız gerekir. Başlatılan MediaCapture nesnesinin Çerçeveler sözlüğüne erişip kullanmak istediğiniz çerçeve kaynağının tanımlayıcısını belirleyerek bu nesneyi alırsınız. Bu nedenle, bir çerçeve kaynak grubu seçerken MediaFrameSourceInfo nesnesini kaydettik.

MediaFrameSource.SupportedFormats özelliği, çerçeve kaynağı için desteklenen biçimleri açıklayan MediaFrameFormat nesnelerinin listesini içerir. Bu örnekte, genişliği 1080 piksel olan ve çerçeveleri 32 bit RGB biçiminde sağlayabilen bir biçim seçilir. FirstOrDefault uzantısı yöntemi listedeki ilk girişi seçer. Seçilen biçim null ise, istenen biçim çerçeve kaynağı tarafından desteklenmez. Biçim destekleniyorsa , SetFormatAsync'i çağırarak kaynağın bu biçimi kullanmasını isteyebilirsiniz.

var colorFrameSource = m_mediaCapture.FrameSources[colorSourceInfo.Id];
var preferredFormat = colorFrameSource.SupportedFormats.Where(format =>
{
    return format.VideoFormat.Width >= 1080
    && format.Subtype == MediaEncodingSubtypes.Argb32;

}).FirstOrDefault();

if (preferredFormat == null)
{
    // Our desired format is not supported
    return;
}

await colorFrameSource.SetFormatAsync(preferredFormat);

Çerçeve kaynağı için çerçeve okuyucu oluşturma

Bir medya çerçevesi kaynağından çerçeveleri almak için MediaFrameReaderkullanın.

MediaFrameReader m_mediaFrameReader;

Başlatılan MediaCapture nesnenizde CreateFrameReaderAsync çağırarak çerçeve okuyucunun örneğini oluşturun. Bu yöntemin ilk bağımsız değişkeni, çerçeveleri almak istediğiniz çerçeve kaynağıdır. Kullanmak istediğiniz her çerçeve kaynağı için ayrı bir çerçeve okuyucu oluşturabilirsiniz. İkinci bağımsız değişken, sisteme çerçevelerin gelmesini istediğiniz çıkış biçimini bildirir. Bu, geldikçe çerçevelere kendi dönüştürmelerinizi yapmanıza gerek kalmadan sizi kurtarabilir. Çerçeve kaynağı tarafından desteklenmeyen bir biçim belirtirseniz bir özel durum oluşturulacağını unutmayın, bu nedenle bu değerin SupportedFormats koleksiyonunda olduğundan emin olun.

Çerçeve okuyucuyu oluşturduktan sonra, kaynaktan yeni bir çerçeve mevcut olduğunda aktive edilen FrameArrived olayı için bir işleyici kaydedin.

Sisteme StartAsync'i çağırarak kaynaktan çerçeveleri okumaya başlamasını söyleyin.

m_mediaFrameReader = await m_mediaCapture.CreateFrameReaderAsync(colorFrameSource, MediaEncodingSubtypes.Argb32);
m_mediaFrameReader.FrameArrived += ColorFrameReader_FrameArrived;
await m_mediaFrameReader.StartAsync();

Çerçeve geldiğinde olayı ele al

MediaFrameReader.FrameArrived olayı her yeni çerçeve mevcut olduğunda tetiklenir. Gelen her çerçeveyi işlemeyi veya yalnızca ihtiyacınız olduğunda çerçeveleri kullanmayı seçebilirsiniz. Çerçeve okuyucu olayı kendi iş parçacığında oluşturduğundan, birden çok iş parçacığından aynı verilere erişmeye çalışmadığınızdan emin olmak için bazı eşitleme mantığı uygulamanız gerekebilir. Bu bölümde, çizim rengi çerçevelerini XAML sayfasındaki bir görüntü denetimiyle nasıl eşitleyebileceğiniz gösterilir. Bu senaryo, ui iş parçacığında XAML denetimlerine yönelik tüm güncelleştirmelerin gerçekleştirilmesini gerektiren ek eşitleme kısıtlamasını ele alır.

XAML'de çerçeveleri görüntülemenin ilk adımı bir Görüntü denetimi oluşturmaktır.

<Image x:Name="iFrameReaderImageControl" MaxWidth="300" MaxHeight="200"/>

Kod arkası sayfanızda, tüm gelen görüntülerin kopyalanacağı bir geri arabellek olarak kullanılacak SoftwareBitmap türünde bir sınıf üyesi değişkeni bildirin. Görüntü verilerinin değil, yalnızca nesne referanslarının kopyalandığını unutmayın. Ayrıca, kullanıcı arabirimi işlemimizin şu anda çalışıp çalışmadığını izlemek için bir boole değeri bildirin.

private SoftwareBitmap backBuffer;
private bool taskRunning = false;

Çerçeveler SoftwareBitmap nesneleri olarak geldiğinden, XAML Denetimi için kaynak olarak SoftwareBitmap kullanmanıza olanak tanıyan bir SoftwareBitmapSource nesnesi oluşturmanız gerekir. Çerçeve okuyucuyu başlatmadan önce görüntü kaynağını kodunuzda bir yere ayarlamanız gerekir.

iFrameReaderImageControl.Source = new SoftwareBitmapSource();

Şimdi FrameArrived olay işleyicisini uygulama zamanı geldi. İşleyici çağrıldığında , sender parametresi olayı tetikleyen MediaFrameReader nesnesine bir başvuru içerir. En son kareyi almaya çalışmak için bu nesnede TryAcquireLatestFrame'i çağırın. Adından da anlaşılacağı gibi, Bir çerçeve döndürmede TryAcquireLatestFrame başarılı olmayabilir. Bu nedenle, VideoMediaFrame ve ardından SoftwareBitmap özelliklerine erişirken null değerini test ettiğinizden emin olun. Bu örnekte null koşullu işleç ? SoftwareBitmap erişmek için kullanılır ve ardından alınan nesnenin null olup olmadığı denetlenir.

Görüntü kontrolü, görüntüleri yalnızca önceden çarpılan veya alfa olmadan BRGA8 formatında görüntüleyebilir. Gelen çerçeve bu biçimde değilse, yazılım bit eşlemini doğru biçime dönüştürmek için Convert statik yöntemi kullanılır .

Ardından, Interlocked.Exchange yöntemi, gelen bitmap başvurusunu backbuffer bitmap ile değiştirmek için kullanılır. Bu yöntem, iş parçacığı güvenli olan atomik bir işlemde bu başvuruları değiştirir. Değiştirme işleminden sonra, kaynaklarını serbest bırakmak için softwareBitmap değişkenindeki eski backbuffer görüntüsü yok edilir.

Ardından, Görüntü öğesiyle ilişkilendirilen CoreDispatcher, kullanıcı arabirimi iş parçacığında çalışacak bir görevi RunAsyncçağrısını kullanarak oluşturmak için kullanılır. Görev içindeki asenkron görevler yürütüleceği için, RunAsync geçirilecek lambda ifadesi async anahtar sözcüğüyle tanımlanmıştır.

Görevin içinde _taskRunning değişkeni, görevin aynı anda yalnızca bir örneğinin çalıştığından emin olmak için denetlendi. Görev henüz çalışmıyorsa, görevin yeniden çalışmasını önlemek için _taskRunning true olarak ayarlanır. döngü sırasında Interlocked.Exchange, backbuffer görüntüsü null olana kadar backbuffer'dan geçici bir SoftwareBitmap kopyalamak için çağrılır. Geçici bit eşlem her doldurulışında, Görüntü Source özelliği bir SoftwareBitmapSource'e gönderilir ve ardından görüntünün kaynağını ayarlamak için SetBitmapAsync çağrılır.

Son olarak , _taskRunning değişkeni false olarak ayarlanır, böylece görev işleyici bir sonraki çağrılışında yeniden çalıştırılabilir.

Uyarı

MediaFrameReferenceVideoMediaFrame özelliği tarafından sağlanan SoftwareBitmap veya Direct3DSurface nesnelerine erişiyorsanız, sistem bu nesnelere güçlü bir referans oluşturur, bu da MediaFrameReferenceüzerinde Dispose çağırdığınızda atılmayacakları anlamına gelir. Nesnelerin hemen atılması için SoftwareBitmap veya Direct3DSurface'inDispose yöntemini açıkça çağırmanız gerekir. Aksi takdirde, atık toplayıcı sonunda bu nesneler için belleği boşaltacaktır, ancak bunun ne zaman gerçekleşeceğini bilemezsiniz ve ayrılan bit eşlemlerin veya yüzeylerin sayısı sistemin izin verdiği maksimum miktarı aşarsa, yeni karelerin akışı durur. Örneğin SoftwareBitmap.Copy yöntemini kullanarak alınan çerçeveleri kopyalayabilir ve ardından bu sınırlamanın üstesinden gelmek için özgün çerçeveleri serbest bırakabilirsiniz. Ayrıca, CreateFrameReaderAsync(Windows.Media.Capture.Frames.MediaFrameSource inputSourceaşırı yüklemesini kullanarak MediaFrameReader oluşturursanız, System.String outputSubtype, Windows.Graphics.Imaging.BitmapSize outputSize) veya CreateFrameReaderAsync(Windows.Media.Capture.Frames.MediaFrameSource inputSource, System.String outputSubtype), döndürülen çerçeveler özgün çerçeve verilerinin kopyalarıdır ve bu nedenle tutulduğunda çerçeve alımının durmasına neden olmazlar.

private void ColorFrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
    var mediaFrameReference = sender.TryAcquireLatestFrame();
    var videoMediaFrame = mediaFrameReference?.VideoMediaFrame;
    var softwareBitmap = videoMediaFrame?.SoftwareBitmap;

    if (softwareBitmap != null)
    {
        if (softwareBitmap.BitmapPixelFormat != Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8 ||
            softwareBitmap.BitmapAlphaMode != Windows.Graphics.Imaging.BitmapAlphaMode.Premultiplied)
        {
            softwareBitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
        }

        // Swap the processed frame to _backBuffer and dispose of the unused image.
        softwareBitmap = Interlocked.Exchange(ref backBuffer, softwareBitmap);
        softwareBitmap?.Dispose();

        // Changes to XAML ImageElement must happen on UI thread through Dispatcher
        var task = iFrameReaderImageControl.DispatcherQueue.TryEnqueue(
            async () =>
            {
                // Don't let two copies of this task run at the same time.
                if (taskRunning)
                {
                    return;
                }
                taskRunning = true;

                // Keep draining frames from the backbuffer until the backbuffer is empty.
                SoftwareBitmap latestBitmap;
                while ((latestBitmap = Interlocked.Exchange(ref backBuffer, null)) != null)
                {
                    var imageSource = (SoftwareBitmapSource)iFrameReaderImageControl.Source;
                    await imageSource.SetBitmapAsync(latestBitmap);
                    latestBitmap.Dispose();
                }

                taskRunning = false;
            });
    }

    if (mediaFrameReference != null)
    {
        mediaFrameReference.Dispose();
    }
}

Kaynakları temizleme

Çerçeveleri okumayı bitirdiğinizde, StopAsyncçağırarak medya çerçevesi okuyucusunu durdurduğunuzdan, FrameArrived işleyicisinin kaydını kaldırdığınızdan ve MediaCapture nesnesini imha ettiğinizden emin olun.

await m_mediaFrameReader.StopAsync();
m_mediaFrameReader.FrameArrived -= ColorFrameReader_FrameArrived;
m_mediaCapture.Dispose();
m_mediaCapture = null;

Uygulamanız askıya alınırken medya yakalama nesnelerini temizleme hakkında daha fazla bilgi için bkz. WinUI 3 uygulamasında kamera önizlemesini gösterme.

FrameRenderer yardımcı sınıfı

Bu bölüm, a için tam kod listesini sağlar ve uygulamanızdaki renk, kızılötesi ve derinlik kaynaklarından kareleri görüntülemeyi kolaylaştıran bir yardımcı sınıf sağlar. Genellikle, derinlik ve kızılötesi verilerle yalnızca ekranda görüntülemekten daha fazlasını yapmak istersiniz, ancak bu yardımcı sınıf çerçeve okuyucu özelliğini göstermek ve kendi çerçeve okuyucu uygulamanızda hata ayıklamak için yararlı bir araçtır. Bu bölümdeki kod Kamera çerçeveleri örneğinden uyarlanmıştır.

FrameRenderer yardımcı sınıfı aşağıdaki yöntemleri uygular.

  • FrameRenderer oluşturucusu - Oluşturucu, medya çerçevelerini görüntülemek için sunduğunuz XAML Image öğesini kullanmak üzere yardımcı sınıfı başlatır.
  • ProcessFrame - Bu yöntem, oluşturucuya iletmiş olduğunuz Görüntü öğesinde MediaFrameReference ile temsil edilen bir medya çerçevesi görüntüler. Bu yöntemi genellikle FrameArrived olay işleyicinizden çağırmalısınız ve TryAcquireLatestFrametarafından döndürülen çerçeveyi iletmelisiniz.
  • ConvertToDisplayableImage - Bu yöntemler medya çerçevesinin biçimini denetler ve gerekirse görüntülenebilir bir biçime dönüştürür. Renkli görüntüler için bu, renk biçiminin BGRA8 olduğundan ve bit eşlem alfa modunun önceden eşlemli olduğundan emin olmak anlamına gelir. Derinlik veya kızılötesi çerçeveler için her tarama çizgisi, örnekte de yer alan ve aşağıda listelenen PsuedoColorHelper sınıfı kullanılarak, derinlik veya kızılötesi değerlerini pseudocolor gradyanlarına dönüştürmek için işlenir.

Uyarı

SoftwareBitmap görüntülerinde piksel işlemesi yapmak için yerel bir bellek arabelleğine erişmeniz gerekir. Bunu yapmak için, aşağıdaki kod listesinde yer alan IMemoryBufferByteAccess COM arabirimini kullanmanız ve güvenli olmayan kodun derlenmesine izin vermek için proje özelliklerinizi güncelleştirmeniz gerekir. Daha fazla bilgi için bkz. Bit eşlem görüntüleri oluşturma, düzenleme ve kaydetme.

[GeneratedComInterface]
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe partial interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}
class FrameRenderer
{
    private Image m_imageElement;
    private SoftwareBitmap m_backBuffer;
    private bool _taskRunning = false;

    public FrameRenderer(Image imageElement)
    {
        m_imageElement = imageElement;
        m_imageElement.Source = new SoftwareBitmapSource();
    }

    // Processes a MediaFrameReference and displays it in a XAML image control
    public void ProcessFrame(MediaFrameReference frame)
    {
        var softwareBitmap = FrameRenderer.ConvertToDisplayableImage(frame?.VideoMediaFrame);
        if (softwareBitmap != null)
        {
            // Swap the processed frame to m_backBuffer and trigger UI thread to render it
            softwareBitmap = Interlocked.Exchange(ref m_backBuffer, softwareBitmap);

            // UI thread always reset m_backBuffer before using it.  Unused bitmap should be disposed.
            softwareBitmap?.Dispose();

            // Changes to xaml ImageElement must happen in UI thread through Dispatcher
            var task = m_imageElement.DispatcherQueue.TryEnqueue(
                async () =>
                {
                    // Don't let two copies of this task run at the same time.
                    if (_taskRunning)
                    {
                        return;
                    }
                    _taskRunning = true;

                    // Keep draining frames from the backbuffer until the backbuffer is empty.
                    SoftwareBitmap latestBitmap;
                    while ((latestBitmap = Interlocked.Exchange(ref m_backBuffer, null)) != null)
                    {
                        var imageSource = (SoftwareBitmapSource)m_imageElement.Source;
                        await imageSource.SetBitmapAsync(latestBitmap);
                        latestBitmap.Dispose();
                    }

                    _taskRunning = false;
                });
        }
    }



    // Function delegate that transforms a scanline from an input image to an output image.
    private unsafe delegate void TransformScanline(int pixelWidth, byte* inputRowBytes, byte* outputRowBytes);
    /// <summary>
    /// Determines the subtype to request from the MediaFrameReader that will result in
    /// a frame that can be rendered by ConvertToDisplayableImage.
    /// </summary>
    /// <returns>Subtype string to request, or null if subtype is not renderable.</returns>

    public static string GetSubtypeForFrameReader(MediaFrameSourceKind kind, MediaFrameFormat format)
    {
        // Note that media encoding subtypes may differ in case.
        // https://docs.microsoft.com/en-us/uwp/api/Windows.Media.MediaProperties.MediaEncodingSubtypes

        string subtype = format.Subtype;
        switch (kind)
        {
            // For color sources, we accept anything and request that it be converted to Bgra8.
            case MediaFrameSourceKind.Color:
                return Windows.Media.MediaProperties.MediaEncodingSubtypes.Bgra8;

            // The only depth format we can render is D16.
            case MediaFrameSourceKind.Depth:
                return String.Equals(subtype, Windows.Media.MediaProperties.MediaEncodingSubtypes.D16, StringComparison.OrdinalIgnoreCase) ? subtype : null;

            // The only infrared formats we can render are L8 and L16.
            case MediaFrameSourceKind.Infrared:
                return (String.Equals(subtype, Windows.Media.MediaProperties.MediaEncodingSubtypes.L8, StringComparison.OrdinalIgnoreCase) ||
                    String.Equals(subtype, Windows.Media.MediaProperties.MediaEncodingSubtypes.L16, StringComparison.OrdinalIgnoreCase)) ? subtype : null;

            // No other source kinds are supported by this class.
            default:
                return null;
        }
    }

    /// <summary>
    /// Converts a frame to a SoftwareBitmap of a valid format to display in an Image control.
    /// </summary>
    /// <param name="inputFrame">Frame to convert.</param>

    public static unsafe SoftwareBitmap ConvertToDisplayableImage(VideoMediaFrame inputFrame)
    {
        SoftwareBitmap result = null;
        using (var inputBitmap = inputFrame?.SoftwareBitmap)
        {
            if (inputBitmap != null)
            {
                switch (inputFrame.FrameReference.SourceKind)
                {
                    case MediaFrameSourceKind.Color:
                        // XAML requires Bgra8 with premultiplied alpha.
                        // We requested Bgra8 from the MediaFrameReader, so all that's
                        // left is fixing the alpha channel if necessary.
                        if (inputBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8)
                        {
                            System.Diagnostics.Debug.WriteLine("Color frame in unexpected format.");
                        }
                        else if (inputBitmap.BitmapAlphaMode == BitmapAlphaMode.Premultiplied)
                        {
                            // Already in the correct format.
                            result = SoftwareBitmap.Copy(inputBitmap);
                        }
                        else
                        {
                            // Convert to premultiplied alpha.
                            result = SoftwareBitmap.Convert(inputBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
                        }
                        break;

                    case MediaFrameSourceKind.Depth:
                        // We requested D16 from the MediaFrameReader, so the frame should
                        // be in Gray16 format.
                        if (inputBitmap.BitmapPixelFormat == BitmapPixelFormat.Gray16)
                        {
                            // Use a special pseudo color to render 16 bits depth frame.
                            var depthScale = (float)inputFrame.DepthMediaFrame.DepthFormat.DepthScaleInMeters;
                            var minReliableDepth = inputFrame.DepthMediaFrame.MinReliableDepth;
                            var maxReliableDepth = inputFrame.DepthMediaFrame.MaxReliableDepth;
                            result = TransformBitmap(inputBitmap, (w, i, o) => PseudoColorHelper.PseudoColorForDepth(w, i, o, depthScale, minReliableDepth, maxReliableDepth));
                        }
                        else
                        {
                            System.Diagnostics.Debug.WriteLine("Depth frame in unexpected format.");
                        }
                        break;

                    case MediaFrameSourceKind.Infrared:
                        // We requested L8 or L16 from the MediaFrameReader, so the frame should
                        // be in Gray8 or Gray16 format. 
                        switch (inputBitmap.BitmapPixelFormat)
                        {
                            case BitmapPixelFormat.Gray16:
                                // Use pseudo color to render 16 bits frames.
                                result = TransformBitmap(inputBitmap, PseudoColorHelper.PseudoColorFor16BitInfrared);
                                break;

                            case BitmapPixelFormat.Gray8:
                                // Use pseudo color to render 8 bits frames.
                                result = TransformBitmap(inputBitmap, PseudoColorHelper.PseudoColorFor8BitInfrared);
                                break;
                            default:
                                System.Diagnostics.Debug.WriteLine("Infrared frame in unexpected format.");
                                break;
                        }
                        break;
                }
            }
        }

        return result;
    }



    /// <summary>
    /// Transform image into Bgra8 image using given transform method.
    /// </summary>
    /// <param name="softwareBitmap">Input image to transform.</param>
    /// <param name="transformScanline">Method to map pixels in a scanline.</param>

    private static unsafe SoftwareBitmap TransformBitmap(SoftwareBitmap softwareBitmap, TransformScanline transformScanline)
    {
        // XAML Image control only supports premultiplied Bgra8 format.
        var outputBitmap = new SoftwareBitmap(BitmapPixelFormat.Bgra8,
            softwareBitmap.PixelWidth, softwareBitmap.PixelHeight, BitmapAlphaMode.Premultiplied);

        using (var input = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Read))
        using (var output = outputBitmap.LockBuffer(BitmapBufferAccessMode.Write))
        {
            // Get stride values to calculate buffer position for a given pixel x and y position.
            int inputStride = input.GetPlaneDescription(0).Stride;
            int outputStride = output.GetPlaneDescription(0).Stride;
            int pixelWidth = softwareBitmap.PixelWidth;
            int pixelHeight = softwareBitmap.PixelHeight;

            using (var outputReference = output.CreateReference())
            using (var inputReference = input.CreateReference())
            {
                // Get input and output byte access buffers.
                byte* inputBytes;
                uint inputCapacity;
                ((IMemoryBufferByteAccess)inputReference).GetBuffer(out inputBytes, out inputCapacity);
                byte* outputBytes;
                uint outputCapacity;
                ((IMemoryBufferByteAccess)outputReference).GetBuffer(out outputBytes, out outputCapacity);

                // Iterate over all pixels and store converted value.
                for (int y = 0; y < pixelHeight; y++)
                {
                    byte* inputRowBytes = inputBytes + y * inputStride;
                    byte* outputRowBytes = outputBytes + y * outputStride;

                    transformScanline(pixelWidth, inputRowBytes, outputRowBytes);
                }
            }
        }

        return outputBitmap;
    }



    /// <summary>
    /// A helper class to manage look-up-table for pseudo-colors.
    /// </summary>

    private static class PseudoColorHelper
    {
        #region Constructor, private members and methods

        private const int TableSize = 1024;   // Look up table size
        private static readonly uint[] PseudoColorTable;
        private static readonly uint[] InfraredRampTable;

        // Color palette mapping value from 0 to 1 to blue to red colors.
        private static readonly Color[] ColorRamp =
        {
            Color.FromArgb(a:0xFF, r:0x7F, g:0x00, b:0x00),
            Color.FromArgb(a:0xFF, r:0xFF, g:0x00, b:0x00),
            Color.FromArgb(a:0xFF, r:0xFF, g:0x7F, b:0x00),
            Color.FromArgb(a:0xFF, r:0xFF, g:0xFF, b:0x00),
            Color.FromArgb(a:0xFF, r:0x7F, g:0xFF, b:0x7F),
            Color.FromArgb(a:0xFF, r:0x00, g:0xFF, b:0xFF),
            Color.FromArgb(a:0xFF, r:0x00, g:0x7F, b:0xFF),
            Color.FromArgb(a:0xFF, r:0x00, g:0x00, b:0xFF),
            Color.FromArgb(a:0xFF, r:0x00, g:0x00, b:0x7F),
        };

        static PseudoColorHelper()
        {
            PseudoColorTable = InitializePseudoColorLut();
            InfraredRampTable = InitializeInfraredRampLut();
        }

        /// <summary>
        /// Maps an input infrared value between [0, 1] to corrected value between [0, 1].
        /// </summary>
        /// <param name="value">Input value between [0, 1].</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]  // Tell the compiler to inline this method to improve performance

        private static uint InfraredColor(float value)
        {
            int index = (int)(value * TableSize);
            index = index < 0 ? 0 : index > TableSize - 1 ? TableSize - 1 : index;
            return InfraredRampTable[index];
        }

        /// <summary>
        /// Initializes the pseudo-color look up table for infrared pixels
        /// </summary>

        private static uint[] InitializeInfraredRampLut()
        {
            uint[] lut = new uint[TableSize];
            for (int i = 0; i < TableSize; i++)
            {
                var value = (float)i / TableSize;
                // Adjust to increase color change between lower values in infrared images

                var alpha = (float)Math.Pow(1 - value, 12);
                lut[i] = ColorRampInterpolation(alpha);
            }

            return lut;
        }



        /// <summary>
        /// Initializes pseudo-color look up table for depth pixels
        /// </summary>
        private static uint[] InitializePseudoColorLut()
        {
            uint[] lut = new uint[TableSize];
            for (int i = 0; i < TableSize; i++)
            {
                lut[i] = ColorRampInterpolation((float)i / TableSize);
            }

            return lut;
        }



        /// <summary>
        /// Maps a float value to a pseudo-color pixel
        /// </summary>
        private static uint ColorRampInterpolation(float value)
        {
            // Map value to surrounding indexes on the color ramp
            int rampSteps = ColorRamp.Length - 1;
            float scaled = value * rampSteps;
            int integer = (int)scaled;
            int index =
                integer < 0 ? 0 :
                integer >= rampSteps - 1 ? rampSteps - 1 :
                integer;

            Color prev = ColorRamp[index];
            Color next = ColorRamp[index + 1];

            // Set color based on ratio of closeness between the surrounding colors
            uint alpha = (uint)((scaled - integer) * 255);
            uint beta = 255 - alpha;
            return
                ((prev.A * beta + next.A * alpha) / 255) << 24 | // Alpha
                ((prev.R * beta + next.R * alpha) / 255) << 16 | // Red
                ((prev.G * beta + next.G * alpha) / 255) << 8 |  // Green
                ((prev.B * beta + next.B * alpha) / 255);        // Blue
        }


        /// <summary>
        /// Maps a value in [0, 1] to a pseudo RGBA color.
        /// </summary>
        /// <param name="value">Input value between [0, 1].</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]

        private static uint PseudoColor(float value)
        {
            int index = (int)(value * TableSize);
            index = index < 0 ? 0 : index > TableSize - 1 ? TableSize - 1 : index;
            return PseudoColorTable[index];
        }

        #endregion

        /// <summary>
        /// Maps each pixel in a scanline from a 16 bit depth value to a pseudo-color pixel.
        /// </summary>
        /// <param name="pixelWidth">Width of the input scanline, in pixels.</param>
        /// <param name="inputRowBytes">Pointer to the start of the input scanline.</param>
        /// <param name="outputRowBytes">Pointer to the start of the output scanline.</param>
        /// <param name="depthScale">Physical distance that corresponds to one unit in the input scanline.</param>
        /// <param name="minReliableDepth">Shortest distance at which the sensor can provide reliable measurements.</param>
        /// <param name="maxReliableDepth">Furthest distance at which the sensor can provide reliable measurements.</param>

        public static unsafe void PseudoColorForDepth(int pixelWidth, byte* inputRowBytes, byte* outputRowBytes, float depthScale, float minReliableDepth, float maxReliableDepth)
        {
            // Visualize space in front of your desktop.
            float minInMeters = minReliableDepth * depthScale;
            float maxInMeters = maxReliableDepth * depthScale;
            float one_min = 1.0f / minInMeters;
            float range = 1.0f / maxInMeters - one_min;

            ushort* inputRow = (ushort*)inputRowBytes;
            uint* outputRow = (uint*)outputRowBytes;

            for (int x = 0; x < pixelWidth; x++)
            {
                var depth = inputRow[x] * depthScale;

                if (depth == 0)
                {
                    // Map invalid depth values to transparent pixels.
                    // This happens when depth information cannot be calculated, e.g. when objects are too close.
                    outputRow[x] = 0;
                }
                else
                {
                    var alpha = (1.0f / depth - one_min) / range;
                    outputRow[x] = PseudoColor(alpha * alpha);
                }
            }
        }



        /// <summary>
        /// Maps each pixel in a scanline from a 8 bit infrared value to a pseudo-color pixel.
        /// </summary>
        /// /// <param name="pixelWidth">Width of the input scanline, in pixels.</param>
        /// <param name="inputRowBytes">Pointer to the start of the input scanline.</param>
        /// <param name="outputRowBytes">Pointer to the start of the output scanline.</param>

        public static unsafe void PseudoColorFor8BitInfrared(
            int pixelWidth, byte* inputRowBytes, byte* outputRowBytes)
        {
            byte* inputRow = inputRowBytes;
            uint* outputRow = (uint*)outputRowBytes;

            for (int x = 0; x < pixelWidth; x++)
            {
                outputRow[x] = InfraredColor(inputRow[x] / (float)Byte.MaxValue);
            }
        }

        /// <summary>
        /// Maps each pixel in a scanline from a 16 bit infrared value to a pseudo-color pixel.
        /// </summary>
        /// <param name="pixelWidth">Width of the input scanline.</param>
        /// <param name="inputRowBytes">Pointer to the start of the input scanline.</param>
        /// <param name="outputRowBytes">Pointer to the start of the output scanline.</param>

        public static unsafe void PseudoColorFor16BitInfrared(int pixelWidth, byte* inputRowBytes, byte* outputRowBytes)
        {
            ushort* inputRow = (ushort*)inputRowBytes;
            uint* outputRow = (uint*)outputRowBytes;

            for (int x = 0; x < pixelWidth; x++)
            {
                outputRow[x] = InfraredColor(inputRow[x] / (float)UInt16.MaxValue);
            }
        }
    }


    // Displays the provided softwareBitmap in a XAML image control.
    public void PresentSoftwareBitmap(SoftwareBitmap softwareBitmap)
    {
        if (softwareBitmap != null)
        {
            // Swap the processed frame to m_backBuffer and trigger UI thread to render it
            softwareBitmap = Interlocked.Exchange(ref m_backBuffer, softwareBitmap);

            // UI thread always reset m_backBuffer before using it.  Unused bitmap should be disposed.
            softwareBitmap?.Dispose();

            // Changes to xaml ImageElement must happen in UI thread through Dispatcher
            var task = m_imageElement.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                async () =>
                {
                    // Don't let two copies of this task run at the same time.
                    if (_taskRunning)
                    {
                        return;
                    }
                    _taskRunning = true;

                    // Keep draining frames from the backbuffer until the backbuffer is empty.
                    SoftwareBitmap latestBitmap;
                    while ((latestBitmap = Interlocked.Exchange(ref m_backBuffer, null)) != null)
                    {
                        var imageSource = (SoftwareBitmapSource)m_imageElement.Source;
                        await imageSource.SetBitmapAsync(latestBitmap);
                        latestBitmap.Dispose();
                    }

                    _taskRunning = false;
                });
        }
    }
}

Birden çok kaynaktan zaman bağıntılı çerçeveler almak için MultiSourceMediaFrameReader kullanma

Windows 10, sürüm 1607'den başlayarak, birden çok kaynaktan zaman bağıntılı çerçeveler almak için MultiSourceMediaFrameReader'ı kullanabilirsiniz. Bu API, DepthCorrelatedCoordinateMapper sınıfını kullanma gibi yakın zamansal yakınlıkta alınan birden çok kaynaktan çerçeve gerektiren işlemlerin yapılmasını kolaylaştırır. Bu yeni yöntemi kullanmanın bir sınırlaması, çerçeveye gelen olayların yalnızca en yavaş yakalama kaynağı hızında tetiklenmiş olmasıdır. Daha hızlı kaynaklardan ek çerçeveler bırakılır. Ayrıca sistem çerçevelerin farklı kaynaklardan farklı oranlarda gelmesini beklediğinden, bir kaynağın çerçeve oluşturmayı tamamen durdurup durdurmadığını otomatik olarak algılamaz. Bu bölümdeki örnek kod, ilişkili çerçeveler uygulamanın belirlediği zaman sınırı içinde ulaşmadıklarında çalıştırılan kendi zaman aşımı mantığınızı oluşturmak için bir olayın nasıl kullanılacağını gösterir.

MultiSourceMediaFrameReader kullanma adımları, bu makalede daha önce açıklanan MediaFrameReader kullanma adımlarına benzer. Bu örnekte bir renk kaynağı ve derinlik kaynağı kullanılır. Her kaynaktan çerçeve seçmek için kullanılacak medya çerçevesi kaynak kimliklerini depolamak için bazı dize değişkenleri bildirin. Ardından, örnek için zaman aşımı mantığını uygulamak için kullanılacak manualResetEventSlim, CancellationTokenSource ve EventHandler bildirin.

private MultiSourceMediaFrameReader m_multiFrameReader = null;
private string m_colorSourceId = null;
private string m_depthSourceId = null;


private readonly ManualResetEventSlim m_frameReceived = new ManualResetEventSlim(false);
private readonly CancellationTokenSource m_tokenSource = new CancellationTokenSource();
public event EventHandler CorrelationFailed;

Bu makalede daha önce açıklanan teknikleri kullanarak, bu örnek senaryo için gerekli renk ve derinlik kaynaklarını içeren bir MediaFrameSourceGroup sorgusu. İstenen çerçeve kaynak grubunu seçtikten sonra, her çerçeve kaynağı için MediaFrameSourceInfo elde edin.

var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var eligibleGroups = allGroups.Select(g => new
{
    Group = g,

    // For each source kind, find the source which offers that kind of media frame,
    // or null if there is no such source.
    SourceInfos = new MediaFrameSourceInfo[]
    {
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Color),
        g.SourceInfos.FirstOrDefault(info => info.SourceKind == MediaFrameSourceKind.Depth)
    }
}).Where(g => g.SourceInfos.Any(info => info != null)).ToList();

if (eligibleGroups.Count == 0)
{
    System.Diagnostics.Debug.WriteLine("No source group with color, depth or infrared found.");
    return;
}

var selectedGroupIndex = 0; // Select the first eligible group
MediaFrameSourceGroup selectedGroup = eligibleGroups[selectedGroupIndex].Group;
MediaFrameSourceInfo colorSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[0];
MediaFrameSourceInfo depthSourceInfo = eligibleGroups[selectedGroupIndex].SourceInfos[1];

Seçilen çerçeve kaynak grubunu başlatma ayarlarına aktararak bir MediaCapture nesnesi oluşturun ve başlatın.

m_mediaCapture = new MediaCapture();

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = selectedGroup,
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
    StreamingCaptureMode = StreamingCaptureMode.Video
};

await m_mediaCapture.InitializeAsync(settings);

MediaCapture nesnesini başlatdıktan sonra, renk ve derinlik kameraları için MediaFrameSource nesnelerini alın. İlgili kaynak için gelen çerçeveyi seçebilmeniz için her kaynağın kimliğini depolayın.

MediaFrameSource colorSource =
    m_mediaCapture.FrameSources.Values.FirstOrDefault(
        s => s.Info.SourceKind == MediaFrameSourceKind.Color);

MediaFrameSource depthSource =
    m_mediaCapture.FrameSources.Values.FirstOrDefault(
        s => s.Info.SourceKind == MediaFrameSourceKind.Depth);

if (colorSource == null || depthSource == null)
{
    System.Diagnostics.Debug.WriteLine("MediaCapture doesn't have the Color and Depth streams");
    return;
}

m_colorSourceId = colorSource.Info.Id;
m_depthSourceId = depthSource.Info.Id;

MultiSourceMediaFrameReader oluşturun ve başlatın; CreateMultiSourceFrameReaderAsync çağırarak okuyucunun kullanacağı çerçeve kaynakları dizisini geçirin. FrameArrived olayı için bir etkinlik işleyicisi kaydedin. Bu örnek, çerçeveleri görüntü denetimine işlemek için bu makalede daha önce açıklanan FrameRenderer yardımcı sınıfını oluşturur. StartAsync'i çağırarak çerçeve okuyucuyu başlatın.

Örnekte daha önce bildirilen CorellationFailed olayı için bir olay işleyicisi kaydedin. Kullanılan medya çerçevesi kaynaklarından biri çerçeve üretmeyi durdurursa bu olayı işaretleyeceğiz. Son olarak, zaman aşımı yardımcı yöntemini çağırmak için Task.Run'ü ayrı bir iş parçacığında çalıştırarak NotifyAboutCorrelationFailureçağırın. Bu yöntemin uygulanması bu makalenin devamında gösterilmiştir.

m_multiFrameReader = await m_mediaCapture.CreateMultiSourceFrameReaderAsync(
    new[] { colorSource, depthSource });

m_multiFrameReader.FrameArrived += MultiFrameReader_FrameArrived;

m_frameRenderer = new FrameRenderer(iFrameReaderImageControl);

MultiSourceMediaFrameReaderStartStatus startStatus =
    await m_multiFrameReader.StartAsync();

if (startStatus != MultiSourceMediaFrameReaderStartStatus.Success)
{
    throw new InvalidOperationException(
        "Unable to start reader: " + startStatus);
}

this.CorrelationFailed += MainWindow_CorrelationFailed;
Task.Run(() => NotifyAboutCorrelationFailure(m_tokenSource.Token));

FrameArrived olayı, MultiSourceMediaFrameReadertarafından yönetilen tüm medya çerçevesi kaynaklarından yeni bir çerçeve kullanılabilir olduğunda tetiklenir. Bu, olayın en yavaş medya kaynağının zamanlamasına göre gerçekleşeceği anlamına gelir. Bir kaynak, daha yavaş bir kaynağın bir çerçeve ürettiği süre içinde birden çok çerçeve üretirse, hızlı kaynaktan ek kareler atılır.

Olayla ilişkili MultiSourceMediaFrameReference'i almak için TryAcquireLatestFrame'i çağırın. TryGetFrameReferenceBySourceIdçağrısı yaparak çerçeve okuyucu başlatıldığında depolanan kimlik dizelerini geçirerek her medya çerçevesi kaynağıyla ilişkilendirilmiş MediaFrameReference alın.

Çerçevelerin geldiğini belirtmek için, ManualResetEventSlim nesnesinin Set yöntemini çağırın. Bu olayı ayrı bir iş parçacığında çalışan NotifyCorrelationFailure yönteminde denetleyeceğiz.

Son olarak, zaman bağıntılı medya çerçeveleri üzerinde herhangi bir işlem gerçekleştirin. Bu örnek yalnızca derinlik kaynağından çerçeveyi görüntüler.

private void MultiFrameReader_FrameArrived(MultiSourceMediaFrameReader sender, MultiSourceMediaFrameArrivedEventArgs args)
{
    using (MultiSourceMediaFrameReference muxedFrame =
        sender.TryAcquireLatestFrame())
    using (MediaFrameReference colorFrame =
        muxedFrame.TryGetFrameReferenceBySourceId(m_colorSourceId))
    using (MediaFrameReference depthFrame =
        muxedFrame.TryGetFrameReferenceBySourceId(m_depthSourceId))
    {
        // Notify the listener thread that the frame has been received.
        m_frameReceived.Set();
        m_frameRenderer.ProcessFrame(depthFrame);
    }
}

NotifyCorrelationFailure yardımcı yöntemi, çerçeve okuyucu başlatıldıktan sonra ayrı bir iş parçacığında çalıştırıldı. Bu yöntemde, alınan çerçeve olayının işaretlenip işaretlenmediğini denetleyin. FrameArrived işleyicisinde, bağıntılı çerçeve kümesi geldiğinde bu olayı ayarlayacağımızı unutmayın. Uygulama tarafından tanımlanan belirli bir süre boyunca - ki 5 saniye makul bir değerdir - etkinliğe bir işaret verilmemişse ve CancellationTokenkullanılarak görev iptal edilmemişse, muhtemelen medya kare kaynaklarından biri kare okumayı durdurmuştur. Bu durumda genellikle çerçeve okuyucuyu kapatmak istersiniz, bu nedenle uygulama tanımlı CorrelationFailed olayını yükseltin . Bu olayın işleyicisinde çerçeve okuyucuyu durdurabilir ve bu makalede daha önce gösterildiği gibi ilişkili kaynakları temizleyebilirsiniz.

private void NotifyAboutCorrelationFailure(CancellationToken token)
{
    // If in 5 seconds the token is not cancelled and frame event is not signaled,
    // correlation is most likely failed.
    if (WaitHandle.WaitAny(new[] { token.WaitHandle, m_frameReceived.WaitHandle }, 5000)
            == WaitHandle.WaitTimeout)
    {
        CorrelationFailed?.Invoke(this, EventArgs.Empty);
    }
}
private async void MainWindow_CorrelationFailed(object sender, EventArgs e)
{
    await m_multiFrameReader.StopAsync();
    m_multiFrameReader.FrameArrived -= MultiFrameReader_FrameArrived;
    m_mediaCapture.Dispose();
    m_mediaCapture = null;
}

Alınan çerçevelerin sırasını korumak için arabelleğe alınmış çerçeve edinim modunu kullanın.

Windows 10, sürüm 1709'dan başlayarak, MediaFrameReader veya MultiSourceMediaFrameReader okuyucularının AcquisitionMode özelliğini, çerçeve kaynağından uygulamanıza geçirilen çerçevelerin sırasını korumak için Arabelleğe Alınan olarak ayarlayabilirsiniz.

m_mediaFrameReader.AcquisitionMode = MediaFrameReaderAcquisitionMode.Buffered;

Varsayılan alma modunda Gerçek zamanlı, uygulamanız önceki bir çerçeve için FrameArrived olayını işlemeye devam ederken kaynaktan birden çok çerçeve alınırsa, sistem en son alınan çerçeveyi uygulamanıza gönderir ve arabellekte bekleyen ek çerçeveleri bırakır. Bu, uygulamanıza her zaman en son kullanılabilir çerçeveyi sağlar. Bu genellikle gerçek zamanlı görüntü işleme uygulamaları için en kullanışlı moddur.

Arabelleğe alınmış alma modunda sistem tüm çerçeveleri arabellekte tutar ve alınan sırayla FrameArrived olayı aracılığıyla uygulamanıza sağlar. Bu modda, sistemin çerçeveler için arabelleği dolduğunda, uygulamanız önceki kareler için FrameArrived olayını tamamlayana kadar sistem yeni çerçeveler almayı durdurur ve arabellekte daha fazla alan açar. Unutmayın ki bu işlem, arabellekte boş alan sağlamak amacını taşır.

MediaPlayerElement içinde çerçeveleri görüntülemek için MediaSource kullanma

Windows sürüm 1709 itibarıyla, MediaFrameReader ile edinilen çerçeveleri doğrudan XAML sayfanızdaki MediaPlayerElement denetiminde görüntüleyebilirsiniz. Bu, MediaSource.CreateFromMediaFrameSource kullanılarak MediaSource nesnesi oluşturarak elde edilir ve bu nesne, MediaPlayerElement ile ilişkilendirilmiş bir MediaPlayer tarafından doğrudan kullanılabilir. MediaPlayer ve MediaPlayerElement ile çalışma hakkında ayrıntılı bilgi için bkz. MediaPlayer ile ses ve video yürütme.

Aşağıdaki kod örnekleri, bir XAML sayfasında ön ve arka kameradan kareleri aynı anda görüntüleyen basit bir uygulamayı gösterir.

İlk olarak, XAML sayfanıza iki MediaPlayerElement denetimi ekleyin.

<MediaPlayerElement x:Name="mediaPlayerElement1" Width="320" Height="240"/>
<MediaPlayerElement x:Name="mediaPlayerElement2" Width="320" Height="240"/>

Ardından, bu makaledeki önceki bölümlerde gösterilen teknikleri kullanarak, ön panel ve arka paneldeki renkli kameralar için MediaFrameSourceInfo nesnelerini içeren bir MediaFrameSourceGroup seçin. MediaPlayer, derinlik veya kızılötesi veriler gibi renk içermeyen biçimlerdeki çerçeveleri otomatik olarak renkli verilere dönüştürmez. Diğer algılayıcı türlerinin kullanılması beklenmeyen sonuçlara neden olabilir.

var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var eligibleGroups = allGroups.Select(g => new
{
    Group = g,

    // For each source kind, find the source which offers that kind of media frame,
    // or null if there is no such source.
    SourceInfos = new MediaFrameSourceInfo[]
    {
        g.SourceInfos.FirstOrDefault(info => info.DeviceInformation?.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front
            && info.SourceKind == MediaFrameSourceKind.Color),
        g.SourceInfos.FirstOrDefault(info => info.DeviceInformation?.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back
            && info.SourceKind == MediaFrameSourceKind.Color)
    }
}).Where(g => g.SourceInfos.Any(info => info != null)).ToList();

if (eligibleGroups.Count == 0)
{
    System.Diagnostics.Debug.WriteLine("No source group with front and back-facing camera found.");
    return;
}

var selectedGroupIndex = 0; // Select the first eligible group
MediaFrameSourceGroup selectedGroup = eligibleGroups[selectedGroupIndex].Group;
MediaFrameSourceInfo frontSourceInfo = selectedGroup.SourceInfos[0];
MediaFrameSourceInfo backSourceInfo = selectedGroup.SourceInfos[1];

Seçili MediaFrameSourceGroup'ı kullanmak için MediaCapture nesnesini başlatın.

m_mediaCapture = new MediaCapture();

var settings = new MediaCaptureInitializationSettings()
{
    SourceGroup = selectedGroup,
    SharingMode = MediaCaptureSharingMode.ExclusiveControl,
    MemoryPreference = MediaCaptureMemoryPreference.Cpu,
    StreamingCaptureMode = StreamingCaptureMode.Video
};
try
{
    await m_mediaCapture.InitializeAsync(settings);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine("MediaCapture initialization failed: " + ex.Message);
    return;
}

Son olarak, MediaCapture nesnesinin FrameSources koleksiyonundaki çerçeve kaynaklarından birini seçmek üzere ilişkiliMediaFrameSourceInfo nesnesinin Id özelliğini kullanarak her çerçeve kaynağı için bir MediaSource oluşturmak üzere MediaSource.CreateFromMediaFrameSource'u çağırın. Yeni bir MediaPlayer nesnesi başlatın ve ardından SetMediaPlayerçağırarak bunu bir MediaPlayerElement'e atayın. Ardından Source özelliğini yeni oluşturulan MediaSource nesnesine ayarlayın.

var frameMediaSource1 = MediaSource.CreateFromMediaFrameSource(m_mediaCapture.FrameSources[frontSourceInfo.Id]);
mediaPlayerElement1.SetMediaPlayer(new Windows.Media.Playback.MediaPlayer());
mediaPlayerElement1.MediaPlayer.Source = frameMediaSource1;
mediaPlayerElement1.AutoPlay = true;

var frameMediaSource2 = MediaSource.CreateFromMediaFrameSource(m_mediaCapture.FrameSources[backSourceInfo.Id]);
mediaPlayerElement2.SetMediaPlayer(new Windows.Media.Playback.MediaPlayer());
mediaPlayerElement2.MediaPlayer.Source = frameMediaSource2;
mediaPlayerElement2.AutoPlay = true;

Çerçeve kaynağı seçmek için video profillerini kullanma

MediaCaptureVideoProfile nesnesiyle temsil edilen kamera profili, kare hızları, çözünürlükler veya HDR yakalama gibi gelişmiş özellikler gibi belirli bir yakalama cihazının sağladığı bir dizi özelliği temsil eder. Yakalama cihazı birden çok profili destekleyerek yakalama senaryonuz için en iyi duruma getirilmiş profili seçmenizi sağlar. Windows 10, sürüm 1803'ten başlayarak, MediaCaptureVideoProfile kullanarak belirli özelliklere sahip bir medya çerçevesi kaynağı seçebilir ve ardından MediaCapture nesnesini başlatabilirsiniz. Aşağıdaki örnek kod, Geniş Renk Gamı (WCG) ile HDR'yi destekleyen bir video profili arar ve seçilen cihazı ve profili kullanmak üzere MediaCapture'ı başlatmak için kullanılabilecek bir MediaCaptureInitializationSettings nesnesi döndürür.

İlk olarak, geçerli cihazda kullanılabilen tüm medya çerçevesi kaynak gruplarının listesini almak için MediaFrameSourceGroup.FindAllAsync'i çağırın. Geçerli kaynak grubu için belirtilen profili destekleyen tüm video profillerini almak amacıyla, her kaynak grubunu döngüden geçirin ve MediaCapture.FindKnownVideoProfiles fonksiyonunu çağırın. Bu durumda profil, WCG foto ile HDR'dir. Ölçütleri karşılayan bir profil bulunursa, yeni bir MediaCaptureInitializationSettings nesnesi oluşturun ve VideoProfile'ü seçilen profile ve VideoDeviceId'i geçerli medya çerçevesi kaynak grubunun Id özelliğine ayarlayın.

IReadOnlyList<MediaFrameSourceGroup> sourceGroups = await MediaFrameSourceGroup.FindAllAsync();
MediaCaptureInitializationSettings settings = null;

foreach (MediaFrameSourceGroup sourceGroup in sourceGroups)
{
    // Find a device that support AdvancedColorPhoto
    IReadOnlyList<MediaCaptureVideoProfile> profileList = MediaCapture.FindKnownVideoProfiles(
                                  sourceGroup.Id,
                                  KnownVideoProfile.HdrWithWcgPhoto);

    if (profileList.Count > 0)
    {
        settings = new MediaCaptureInitializationSettings();
        settings.VideoProfile = profileList[0];
        settings.VideoDeviceId = sourceGroup.Id;
        break;
    }
}

Kamera profillerini kullanma hakkında daha fazla bilgi için bkz . Kamera profilleri.