Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questo articolo illustra come usare l'interfaccia IMediaEncodingProperties per impostare la risoluzione e la frequenza dei fotogrammi del flusso di anteprima della fotocamera e di foto e video acquisiti. Viene inoltre illustrato come assicurarsi che le proporzioni del flusso di anteprima corrispondano a quelle del media acquisito.
I profili fotocamera offrono un meccanismo di livello superiore più semplice per l'individuazione e l'impostazione delle proprietà del flusso della fotocamera, ma non sono supportate per tutti i dispositivi. Per altre informazioni, vedere Profili fotocamera.
Determinare se i flussi di anteprima e acquisizione sono indipendenti
In alcuni dispositivi, lo stesso pin hardware viene usato sia per i flussi di anteprima che per i flussi di acquisizione. In questi dispositivi, l'impostazione delle proprietà di codifica, ad esempio il formato, la risoluzione e la frequenza dei fotogrammi su uno avrà effetto su entrambi. Nei dispositivi che usano pin hardware diversi per l'acquisizione e l'anteprima, è possibile impostare le proprietà per ogni flusso in modo indipendente. Usare il codice seguente per determinare se i flussi di anteprima e acquisizione sono indipendenti. Questo esempio imposta una variabile globale booleana che può essere usata per cambiare il comportamento dell'app se i flussi sono condivisi o indipendenti.
if (m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.AllStreamsIdentical ||
m_mediaCapture.MediaCaptureSettings.VideoDeviceCharacteristic == VideoDeviceCharacteristic.PreviewRecordStreamsIdentical)
{
m_captureAndPreviewStreamsIdentical = true;
}
Classe di supporto delle proprietà di codifica multimediale
La creazione di una semplice classe helper per eseguire il wrapping delle funzionalità dell'interfaccia IMediaEncodingProperties semplifica la selezione di un set di proprietà di codifica che soddisfano criteri specifici. Questa classe helper è particolarmente utile a causa del comportamento seguente della funzionalità delle proprietà di codifica:
Nota
Il metodo VideoDeviceController.GetAvailableMediaStreamProperties accetta un membro dell'enumerazione MediaStreamType, ad esempio VideoRecord o Photo, e restituisce un elenco di oggetti ImageEncodingProperties o VideoEncodingProperties che indicano le impostazioni di codifica del flusso, come la risoluzione della foto o del video acquisito. I risultati della chiamata di GetAvailableMediaStreamProperties possono includere ImageEncodingProperties o VideoEncodingProperties indipendentemente dal valore specificato in MediaStreamType. Per questo motivo, è consigliabile controllare sempre il tipo di ogni valore restituito ed eseguirne il cast al tipo appropriato prima di tentare di accedere a uno dei valori delle proprietà.
La classe helper definita di seguito gestisce il controllo dei tipi e il casting per ImageEncodingProperties o VideoEncodingProperties, in modo tale che il codice della tua app non debba distinguere tra questi due tipi. Oltre a questo, la classe helper espone proprietà per il rapporto d'aspetto delle proprietà, la frequenza dei fotogrammi (solo per le proprietà di codifica video) e un nome amichevole che semplifica la visualizzazione delle proprietà di codifica nella tua interfaccia utente.
class StreamPropertiesHelper
{
private IMediaEncodingProperties _properties;
public StreamPropertiesHelper(IMediaEncodingProperties properties)
{
if (properties == null)
{
throw new ArgumentNullException(nameof(properties));
}
// This helper class only uses ImageEncodingProperties or VideoEncodingProperties
if (!(properties is ImageEncodingProperties) && !(properties is VideoEncodingProperties))
{
throw new ArgumentException("Argument is of the wrong type. Required: " + typeof(ImageEncodingProperties).Name
+ " or " + typeof(VideoEncodingProperties).Name + ".", nameof(properties));
}
// Store the actual instance of the IMediaEncodingProperties for setting them later
_properties = properties;
}
public uint Width
{
get
{
if (_properties is ImageEncodingProperties)
{
return (_properties as ImageEncodingProperties).Width;
}
else if (_properties is VideoEncodingProperties)
{
return (_properties as VideoEncodingProperties).Width;
}
return 0;
}
}
public uint Height
{
get
{
if (_properties is ImageEncodingProperties)
{
return (_properties as ImageEncodingProperties).Height;
}
else if (_properties is VideoEncodingProperties)
{
return (_properties as VideoEncodingProperties).Height;
}
return 0;
}
}
public uint FrameRate
{
get
{
if (_properties is VideoEncodingProperties)
{
if ((_properties as VideoEncodingProperties).FrameRate.Denominator != 0)
{
return (_properties as VideoEncodingProperties).FrameRate.Numerator /
(_properties as VideoEncodingProperties).FrameRate.Denominator;
}
}
return 0;
}
}
public double AspectRatio
{
get { return Math.Round((Height != 0) ? (Width / (double)Height) : double.NaN, 2); }
}
public IMediaEncodingProperties EncodingProperties
{
get { return _properties; }
}
public string GetFriendlyName(bool showFrameRate = true)
{
if (_properties is ImageEncodingProperties ||
!showFrameRate)
{
return Width + "x" + Height + " [" + AspectRatio + "] " + _properties.Subtype;
}
else if (_properties is VideoEncodingProperties)
{
return Width + "x" + Height + " [" + AspectRatio + "] " + FrameRate + "FPS " + _properties.Subtype;
}
return String.Empty;
}
}
Ottieni un elenco delle proprietà disponibili del flusso
Ottenere un elenco delle proprietà del flusso disponibili per un dispositivo di acquisizione recuperando il VideoDeviceController per l'oggetto MediaCapture della tua app e quindi chiamando GetAvailableMediaStreamProperties e passando uno dei valori MediaStreamType, VideoPreview, VideoRecordo Photo. In questo esempio, un elenco di oggetti StreamPropertiesHelper, definiti in precedenza in questo articolo, viene creato per ogni valore di IMediaEncodingProperties restituito da GetAvailableMediaStreamProperties. In questo esempio vengono ordinate le proprietà restituite in base prima alla risoluzione e quindi alla frequenza dei fotogrammi.
Se l'app ha requisiti specifici di risoluzione o frequenza dei fotogrammi, puoi selezionare un set di proprietà di codifica multimediale a livello di codice. Un'app della fotocamera tipica espone invece l'elenco delle proprietà disponibili nell'interfaccia utente e consente all'utente di selezionare le impostazioni desiderate. Viene creato un ComboBoxItem per ogni elemento nella lista di oggetti StreamPropertiesHelper. Il contenuto viene impostato sul nome descrittivo restituito dalla classe ausiliaria e il tag viene impostato sulla classe stessa in modo da poter essere utilizzata successivamente per recuperare le proprietà di codifica associate. Ogni ComboBoxItem viene quindi aggiunto a un ComboBox definito nell'interfaccia utente.
private void bGetStreamProperties_Click(object sender, RoutedEventArgs e)
{
// Query all properties of the specified stream type
IEnumerable<StreamPropertiesHelper> allStreamProperties =
m_mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).Select(x => new StreamPropertiesHelper(x));
// Order them by resolution then frame rate
allStreamProperties = allStreamProperties.OrderByDescending(x => x.Height * x.Width).ThenByDescending(x => x.FrameRate);
// Populate the combo box with the entries
foreach (var property in allStreamProperties)
{
ComboBoxItem comboBoxItem = new ComboBoxItem();
comboBoxItem.Content = property.GetFriendlyName();
comboBoxItem.Tag = property;
cbStreamProperties.Items.Add(comboBoxItem);
}
}
Impostare le proprietà del flusso desiderate
Indicare al controller del dispositivo video di usare le proprietà di codifica desiderate chiamando SetMediaStreamPropertiesAsync, passando il valore MediaStreamType che indica se devono essere impostate le proprietà di foto, video o anteprima. In questo esempio viene utilizzata la ComboBox popolata nell'esempio della sezione precedente, in cui le proprietà del flusso multimediale vengono recuperate dalla proprietà tag dell'elemento selezionato.
private async void bSetStreamProperties_Click(object sender, RoutedEventArgs e)
{
if (m_exclusiveCameraAccess)
{
var selectedItem = cbStreamProperties.SelectedItem as ComboBoxItem;
var encodingProperties = (selectedItem.Tag as StreamPropertiesHelper).EncodingProperties;
await m_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoPreview, encodingProperties);
}
}
Tieni presente che l'app deve avere il controllo esclusivo del dispositivo di acquisizione per modificare le proprietà del flusso multimediale.
Abbina le proporzioni dei flussi di anteprima e acquisizione
Un'applicazione fotocamera tipica fornirà all'utente un'interfaccia per selezionare la risoluzione di acquisizione video o foto, ma imposterà automaticamente la risoluzione dell'anteprima. Esistono alcune strategie diverse per selezionare la migliore risoluzione del flusso di anteprima per l'app:
Selezionare la risoluzione di anteprima più elevata disponibile, consentendo al framework dell'interfaccia utente di eseguire qualsiasi ridimensionamento necessario dell'anteprima.
Selezionare la risoluzione di anteprima più vicina alla risoluzione di acquisizione in modo che l'anteprima visualizzi la rappresentazione più vicina al supporto acquisito finale.
Selezionare la risoluzione dell'anteprima più vicina alle dimensioni del CaptureElement in modo che non vengano trasmessi più pixel del necessario nella pipeline del flusso di anteprima.
Nota
In alcuni dispositivi è possibile impostare proporzioni diverse per il flusso di anteprima della fotocamera e il flusso di acquisizione. Il ritaglio dei fotogrammi a causa di questa discrepanza può comportare la presenza di contenuti nel supporto acquisito che non erano visibili nell'anteprima, il che può portare a un'esperienza utente negativa. È fortemente consigliato usare lo stesso rapporto d'aspetto, all'interno di una piccola finestra di tolleranza, per i flussi di anteprima e acquisizione. È bene avere risoluzioni completamente diverse abilitate per l'acquisizione e l'anteprima, purché le proporzioni corrispondano strettamente.
Per assicurarsi che i flussi di acquisizione di foto o video corrispondano alle proporzioni del flusso di anteprima, questo esempio chiama VideoDeviceController.GetMediaStreamProperties e passa il valore di enumerazione VideoPreview per richiedere le proprietà correnti del flusso per il flusso di anteprima. Successivamente viene definita una piccola finestra di tolleranza delle proporzioni in modo da poter includere proporzioni che non corrispondono esattamente al flusso di anteprima, purché siano vicine. Successivamente, vengono selezionati gli oggetti StreamPropertiesHelper in cui le proporzioni si trovano all'interno dell'intervallo di tolleranza definito del flusso di anteprima.
// Query all properties of the specified stream type
IEnumerable<StreamPropertiesHelper> allVideoProperties =
m_mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).Select(x => new StreamPropertiesHelper(x));
// Query the current preview settings
StreamPropertiesHelper previewProperties = new StreamPropertiesHelper(m_mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview));
// Get all formats that have the same-ish aspect ratio as the preview
// Allow for some tolerance in the aspect ratio comparison
const double ASPECT_RATIO_TOLERANCE = 0.015;
var matchingFormats = allVideoProperties.Where(x => Math.Abs(x.AspectRatio - previewProperties.AspectRatio) < ASPECT_RATIO_TOLERANCE);
// Order them by resolution then frame rate
allVideoProperties = matchingFormats.OrderByDescending(x => x.Height * x.Width).ThenByDescending(x => x.FrameRate);
// Clear out old entries and populate the video combo box with new matching entries
cbStreamProperties.Items.Clear();
foreach (var property in allVideoProperties)
{
ComboBoxItem comboBoxItem = new ComboBoxItem();
comboBoxItem.Content = property.GetFriendlyName();
comboBoxItem.Tag = property;
cbStreamProperties.Items.Add(comboBoxItem);
}
SnippetMatchPreviewAspectRatio