Запись звука, видео, снимков экрана и метаданных из игрыCapture game audio, video, screenshots, and metadata

В этой статье рассказывается, как выполнять запись видео, звука и снимков экрана в играх, а также как отправлять метаданные, которые система добавит в записанное и транслируемое мультимедийное содержимое, позволяя вашим и другим приложениям обеспечивать динамичное взаимодействие, синхронизируемое с событиями игрового процесса.This article describes how to capture game video, audio, and screenshots, and how to submit metadata that the system will embed in captured and broadcast media, allowing your app and others to create dynamic experiences that are synchronized to gameplay events.

Существует два разных способа записи процесса игры в приложении UWP.There are two different ways that gameplay can be captured in a UWP app. Пользователь может запустить запись, используя встроенный системный пользовательский интерфейс.The user can initiate capture using the built-in system UI. Носитель, захваченный с помощью этого метода, принимается в экосистему игр Майкрософт, может просматриваться и предоставляться для общего доступа с помощью таких интерфейсов, как приложение Xbox, и не выделенные непосредственно приложению или пользователям.Media that is captured using this technique is ingested into the Microsoft gaming ecosystem, can be viewed and shared through first-party experiences such as the Xbox app, and is not directly availble to your app or to users. В начальных разделах этой статьи рассказывается, как включать и отключать реализованные в системе средства записи работы приложения и как получать уведомления, когда запись запускается или останавливается.The first sections of this article will show you how to enable and disable system-implemented app capture and how to receive notifications when app capture starts or stops.

Другой способ записи мультимедийного содержимого — использовать API-интерфейсы пространства имен Windows.Media.AppRecording.The other way to capture media is to use the APIs of the Windows.Media.AppRecording namespace. Если на устройстве включена возможность записи, ваше приложение можно начать запись игрового процесса, и затем по прошествии некоторого времени вы можете остановить запись, после чего мультимедийное содержимое записывается в файл.If capturing is enabled on the device, your app can start capturing gameplay and then, after some time has passed, you can stop the capture, at which point the media is written to a file. Если пользователь включил возможность записи из истории, можно также записывать уже прошедший игровой процесс, указав время начала записи в прошлом и ее длительность.If the user has enabled historical capture, then you can also record gameplay that has already occured by specifying a start time in the past and a duration to record. Оба этих метода создают видеофайл, доступный вашему приложению, а также, в зависимости от того, где вы решите сохранять файлы, пользователю.Both of these techniques produce an video file that can be accessed by your app, and depending on where you choose to save the files, by the user. В середине этой статьи подробно рассматривается реализация этих сценариев.The middle sections of this article walk you through the implemenation of these scenarios.

Пространство имен Windows.Media.Capture предоставляет API-интерфейсы для создания метаданных, описывающих записываемый или транслируемый игровой процесс.The Windows.Media.Capture namespace provides APIs for creating metadata that describes the gameplay being captured or broadcast. Сюда могут включаться текстовые и числовые значения с текстовыми метками для обозначения каждого элемента данных.This can include text or numeric values, with a text label identifying each data item. Метаданные могут представлять «событие», которое происходит в определенный момент, например когда пользователь заканчивает круг в гонке, либо «состояние», которое сохраняется некоторое время, например игровая карта, на которой находится игрок.Metadata can represent an "event" which occurs at a single moment, such as when the user finishes a lap in a racing game, or it can represent a "state" that persists over a span of time, such as the current game map the user is playing in. Метаданные записываются в кэш, выделенный вашему приложению системой и управляемый ей.The metadata is written to a cache that is allocated and managed for your app by the system. Метаданные добавляются в трансляции и записанные видеофайлы, создаваемые как с помощью встроенных системных средств записи, так и собственных средств записи в приложениях.The metadata is embedded into broadcast streams and captured video files, including both the built-in system capture or custom app capture techniques. В заключительных разделах этой статьи рассказывается, как записывать метаданные игрового процесса.The final sections of this article show you how to write gameplay metadata.

Примечание

Поскольку метаданные игрового процесса могут добавляться в мультимедиа-файлы, которые могут передаваться по сети вне контроля пользователя, не следует включать личные сведения и другую потенциально закрытую информацию в метаданные.Because the gameplay metadata can be embedded in media files that can potentially be shared over the network, out of the user's control, you should not include personally identifiable information or other potentially sensitive data in the metadata.

Включение и отключение системных средств записи работы приложенияEnable and disable system app capture

Системная запись работы приложения запускается пользователем с помощью встроенного пользовательского интерфейса.System app capture is initiated by the user with the built-in system UI. Эти файлы принимаются в Windows Gaming экосистема и недоступны для вашего приложения или пользователя, за исключением возможностей, таких как приложение Xbox.The files are ingested by the Windows gaming ecosystem and is not available to your app or the user, except for through first party experiences like the Xbox app. Ваше приложение может отключить и включить инициируемую системой запись, что позволяет запретить пользователям записывать определенное содержимое или игровой процесс.Your app can disable and enable system-initiated app capture, allowing you to prevent the user from capturing certain content or gameplay.

Чтобы включить или отключить системную запись приложения, просто вызовите статический метод AppCapture.SetAllowedAsync и передайте false для отключения записи или true для ее включения.To enable or disable system app capture, simply call the static method AppCapture.SetAllowedAsync and passing false to disable capture or true to enable capture.

Windows::Media::Capture::AppCapture::SetAllowedAsync(allowed);

Получение уведомлений при запуске и остановке системной записи работы приложенияReceive notifications when system app capture starts and stops

Чтобы получать уведомления, когда системная запись приложения начинается или заканчивается, необходимо сначала получить экземпляр класса AppCapture, вызвав фабричный метод GetForCurrentView.To receive a notification when system app capture begins or ends, first get an instance of the AppCapture class by calling the factory method GetForCurrentView. Затем зарегистрируйте обработчик события CapturingChanged.Next, register a handler for the CapturingChanged event.

Windows::Media::Capture::AppCapture^ appCapture = Windows::Media::Capture::AppCapture::GetForCurrentView();
appCapture->CapturingChanged +=
    ref new TypedEventHandler<Windows::Media::Capture::AppCapture^, Platform::Object^>(this, &App::OnCapturingChanged);

В обработчике для события CapturingChanged можно проверить свойства IsCapturingAudio и IsCapturingVideo, чтобы определить, ведется ли запись звука и видео, соответственно.In the handler for the CapturingChanged event, you can check the IsCapturingAudio and the IsCapturingVideo properties to determine if audio or video are being captured respectively. Возможно, потребуется обновление пользовательского интерфейса приложения для отражения текущего состояния записи.You may want to update your app's UI to indicate the current capturing status.

void App::OnCapturingChanged(Windows::Media::Capture::AppCapture^ sender, Platform::Object^ args)
{
    Platform::String^ captureStatusText = "";
    if (sender->IsCapturingAudio)
    {
        captureStatusText += "Capturing audio.";
    }
    if (sender->IsCapturingVideo)
    {
        captureStatusText += "Capturing video.";
    }
    UpdateStatusText(captureStatusText);
}

Добавление расширений рабочего стола Windows для UWP в приложениеAdd the Windows Desktop Extensions for the UWP to your app

API-интерфейсы для записи звука и видео и захвата снимков экрана непосредственно из приложения, предлагаемые в пространстве имен Windows.Media.AppRecording, не включаются в универсальный контракт API.The APIs for recording audio and video and for capturing screenshots directly from your app, found in the Windows.Media.AppRecording namespace, are not included in the Universal API contract. Для доступа к этим API необходимо добавить ссылку на расширения рабочего стола Windows для UWP в приложение, выполнив следующие действия.To access the APIs, you must add a reference to the Windows Desktop Extensions for the UWP to your app with the following steps.

  1. В Visual Studio в обозревателе решений разверните свой проект UWP и щелкните правой кнопкой мыши папку Ссылки, а затем выберите пункт Добавить ссылку....In Visual Studio, in Solution Explorer, expand your UWP project and right-click References and then select Add Reference....
  2. Разверните узел универсальных приложений для Windows и выберите Расширения.Expand the Universal Windows node and select Extensions.
  3. В списке расширений установите флажок рядом с пунктом расширения рабочего стола Windows для UWP, который соответствует целевой сборке для проекта.In the list of extensions check the checkbox next to the Windows Desktop Extensions for the UWP entry that matches the target build for your project. Для функций трансляции приложения версия должна быть 1709 или более поздней.For the app broadcast features, the version must be 1709 or greater.
  4. Нажмите кнопку ОК.Click OK.

Получите экземпляр AppRecordingManagerGet an instance of AppRecordingManager

Класс AppRecordingManager является центральным API, который вы будете использовать для управления записью в приложении.The AppRecordingManager class is the central API you will use to manage app recording. Получите экземпляр этого класса, вызвав фабричный метод GetDefault.Get an instance of this class by calling the factory method GetDefault. Прежде чем использовать какие-либо API-интерфейсы в пространстве имен Windows.Media.AppRecording, следует проверять их наличие на текущем устройстве.Before using any of the APIs in the Windows.Media.AppRecording namespace, you should check for their presence on the current device. API-интерфейсы недоступны на устройствах под управлением версий ОС более ранних, чем Windows 10, версия 1709.The APIs are not available on devices running an OS version earlier than Windows 10, version 1709. Вместо того чтобы проверить наличие определенной версии операционной системы, используйте метод ApiInformation.IsApiContractPresent для запроса Windows.Media.AppBroadcasting.AppRecordingContract версии 1.0.Rather than check for a specific OS version, use the ApiInformation.IsApiContractPresent method to query for the Windows.Media.AppBroadcasting.AppRecordingContract version 1.0. Если этот контракт представлен, API-интерфейсы записи доступны на этом устройстве.If this contract is present, then the recording APIs are available on the device. В примере кода в этой статье сначала проверяется наличие API, а затем проверяется, имеет ли AppRecordingManager значение null, перед выполнением последующих операций.The example code in this article checks for the APIs once and then checks if the AppRecordingManager is null before subsequent operations.

if (Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent(
    "Windows.Media.AppRecording.AppRecordingContract", 1, 0))
{
    m_appRecordingManager = AppRecordingManager::GetDefault();
}

Определение возможности записи вашего приложенияDetermine if your app can currently record

Существует несколько причин, по которым приложение может в определенный момент не иметь возможности записывать звук или видео; в их число входит несоответствие устройства требованиям к оборудованию для записи и осуществление трансляции другим приложением.There are several reasons that your app may not currently be able to capture audio or video, including if the current device doesn't meet the hardware requirements for recording or if another app is currently broadcasting. Перед началом записи можно проверить, имеет ли ваше приложение возможность записи в данный момент.Before initiating a recording, you can check to see if your app is currently able to record. Вызовите метод GetStatus объекта AppRecordingManager, а затем проверьте свойство CanRecord возвращенного объекта AppRecordingStatus.Call the GetStatus method of the AppRecordingManager object and then check the CanRecord property of the returned AppRecordingStatus object. Если канрекорд возвращает значение false, то есть приложение не может выполнить запись, можно проверить свойство Details , чтобы определить причину.If CanRecord returns false, meaning that your app can't currently record, you can check the Details property to determine the reason. В зависимости от причины, возможно, вы решите отобразить состояние пользователю или показать инструкции для включения записи в приложении.Depending on the reason, you may want to display the status to the user or show instructions for enabling app recording.

bool App::CanRecord()
{

    if (m_appRecordingManager == nullptr)
    {
        return false;
    }

    AppRecordingStatus^ recordingStatus = m_appRecordingManager->GetStatus();

    if (!recordingStatus->CanRecord)
    {
        AppRecordingStatusDetails^ details = recordingStatus->Details;
    
        if (details->IsAnyAppBroadcasting)
        {
            UpdateStatusText("Another app is currently broadcasting.");
            return false;
        }

        if (details->IsCaptureResourceUnavailable)
        {
            UpdateStatusText("The capture resource is currently unavailable.");
            return false;
        }

        if (details->IsGameStreamInProgress)
        {
            UpdateStatusText("A game stream is currently in progress.");
            return false;
        }

        if (details->IsGpuConstrained)
        {
            // Typically, this means that the GPU software does not include an H264 encoder
            UpdateStatusText("The GPU does not support app recording.");
            return false;
        }

        
        if (details->IsAppInactive)
        {
            // Broadcasting can only be started when the application's window is the active window.
            UpdateStatusText("The app window to be recorded is not active.");
            return false;
        }

        if (details->IsBlockedForApp)
        {
            UpdateStatusText("Recording is blocked for this app.");
            return false;
        }

        if (details->IsDisabledByUser)
        {
            UpdateStatusText("The user has disabled GameBar in Windows Settings.");
            return false;
        }

        if (details->IsDisabledBySystem)
        {
            UpdateStatusText("Recording is disabled by the system.");
            return false;
        }

        
        return false;
    }


    return true;
}

Запуск и остановка записи работы приложения в файл вручнуюManually start and stop recording your app to a file

Убедившись, что ваше приложение может вести запись, можно начать новую запись, вызвав метод StartRecordingToFileAsync объекта AppRecordingManager.After verifying that your app is able to record, you can start a new recording by calling the StartRecordingToFileAsync method of the AppRecordingManager object.

В следующем примере первый блок then выполняется в случае сбоя асинхронной задачи.In the following example, the first then block executes when the asynchronous task fails. Второй блок then пытается получить доступ к результату задачи, и, если результат равен null, значит, задача завершена.The second then block attempts to access the result of the task and, if the result is null, then the task has completed. В обоих случаях для обработки результата вызывается вспомогательный метод OnRecordingComplete, показанный ниже.In both cases, the OnRecordingComplete helper method, shown below, is called to handle the result.

void App::StartRecordToFile(Windows::Storage::StorageFile^ file)
{

    if (m_appRecordingManager == nullptr)
    {
        return;
    }

    if (!CanRecord())
    {
        return;
    }


    // Start a recording operation to record starting from 
    // now until the operation fails or is cancelled. 
    m_recordOperation = m_appRecordingManager->StartRecordingToFileAsync(file);

    create_task(m_recordOperation).then(
        [this](AppRecordingResult^ result)
    {
        OnRecordingComplete();
    }).then([this](task<void> t)
    {
        try
        {
            t.get();
        }
        catch (const task_canceled&)
        {
            OnRecordingComplete();
        }
    });
}

После завершения операции записи проверьте свойство Succeeded возвращенного объекта AppRecordingResult, чтобы определить успешно ли прошла операция записи.When the recording operation completes, check the Succeeded property of the returned AppRecordingResult object to determine if the record operation was successful. Если успешно, вы можете проверить свойство IsFileTruncated, чтобы определить, не пришлось ли системе принудительно обрезать записанный файл в связи с нехваткой пространства.If so, you can check the IsFileTruncated property to determine if, for storage reasons, the system was forced to truncate the captured file. Вы можете проверить свойство Duration для определения фактической продолжительности записанного файла, если файл обрезан, его длительность может быть меньше продолжительности операции записи.You can check the Duration property to discover the actual duration of the recorded file which, if the file is truncated, may be shorter than the duration of the recording operation.

void App::OnRecordingComplete()
{
    if (m_recordOperation)
    {
        auto result = m_recordOperation->GetResults();

        if (result->Succeeded)
        {
            Windows::Foundation::TimeSpan duration = result->Duration;
            boolean isTruncated = result->IsFileTruncated;

            UpdateStatusText("Recording completed.");
        }
        else
        {
            // If the recording failed, ExtendedError 
            // can be retrieved and used for diagnostic purposes 
            HResult extendedError = result->ExtendedError;
            LogTelemetryMessage("Error during recording: " + extendedError);
        }

        m_recordOperation = nullptr;
    }
}

Ниже приведены примеры базового кода для запуска и остановки операции записи, показанной в предыдущем примере.The following examples show some basic code for starting and stopping the recording operation shown in the previous example.

StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
concurrency::create_task(storageFolder->CreateFileAsync("recordtofile_example.mp4", CreationCollisionOption::ReplaceExisting)).then(
    [this](StorageFile^ file)
{
    StartRecordToFile(file);
});
void App::FinishRecordToFile()
{
    m_recordOperation->Cancel();
}

Запись исторического отрезка в файлRecord a historical time span to a file

Если пользователь включил возможность записи исторических отрезков для вашего приложения в параметрах системы, вы можете записать отрезок игрового процесса, который проходил ранее.If the user has enabled historical recording for your app in the system settings, you can record a time span of gameplay that has previously transpired. В предыдущем примере в этой статье показано, как убедиться, что ваше приложение в данный момент может записывать игровой процесс.A previous example in this article showed how to confirm that your app can currently record gameplay. Существует дополнительная проверка для того, чтобы определить, включена ли возможность записи исторического отрезка.There is an additional check to determine if historical capture is enabled. Снова вызовите GetStatus и проверьте свойство CanRecordTimeSpan возвращенного объекта AppRecordingStatus.Once again, call GetStatus and check the CanRecordTimeSpan property of the returned AppRecordingStatus object. В этом примере также возвращается свойство HistoricalBufferDuration объекта AppRecordingStatus, которое будет использоваться для определения допустимого времени запуска операции записи.This example also returns the HistoricalBufferDuration property of the AppRecordingStatus which will be used to determine a valid start time for the recording operation.

bool App::CanRecordTimeSpan(TimeSpan &historicalDurationBuffer)
{

    if (m_appRecordingManager == nullptr)
    {
        return false;
    }

    AppRecordingStatus^ recordingStatus = m_appRecordingManager->GetStatus();
    if (recordingStatus->Details->IsTimeSpanRecordingDisabled)
    {
        UpdateStatusText("Historical time span recording is disabled by the system.");
        return false;
    }

    historicalDurationBuffer = recordingStatus->HistoricalBufferDuration;

    return true;
}

Чтобы записать исторический отрезок, необходимо указать время начала записи и ее длительность.To capture a historical timespan, you must specify a start time for the recording and a duration. Время начала предоставляется в виде структуры DateTime.The start time is provided as a DateTime struct. Время начала должно быть раньше текущего времени в пределах продолжительности буфера истории для записи.The start time must be a time before the current time, within the length of the historical recording buffer. В этом примере длина буфера определяется в ходе проверки того, включена ли возможность записи из истории, как показано в предыдущем примере кода.For this example, the buffer length is retrieved as part of the check to see if historical recording is enabled, which is shown in the previous code example. Длительность истории для записи предоставляется в виде структуры TimeSpan, и она также должна быть меньше либо равна длительности исторического буфера.The duration of the historical recording is provided as TimeSpan struct, which should also be equal to or smaller than the duration of the historical buffer. После определения нужного времени и продолжительности вызовите RecordTimeSpanToFileAsync для запуска операции записи.Once you have determined your desired start time and duration, call RecordTimeSpanToFileAsync to start the recording operation.

Как и в случае с ручным запуском и остановкой записи, по завершении записи из истории можно проверить свойство Succeeded возвращенного объекта AppRecordingResult, чтобы определить, завершилась ли операция записи успешно, а также можно проверить свойства IsFileTruncated и Duration для определения фактической продолжительности записанного файла, которая может быть меньше заданной продолжительности, если файл обрезан.Like recording with manual start and stop, when a historical recording completes, you can check the Succeeded property of the returned AppRecordingResult object to determine if the record operation was successful, and you can check the IsFileTruncated and Duration property to discover the actual duration of the recorded file which, if the file is truncated, may be shorter than the duration of the requested time window.

void App::RecordTimeSpanToFile(Windows::Storage::StorageFile^ file)
{


    if (m_appRecordingManager == nullptr)
    {
        return;
    }

    if (!CanRecord())
    {
        return;
    }

    Windows::Foundation::TimeSpan historicalBufferDuration;
    if (!CanRecordTimeSpan(historicalBufferDuration))
    {
        return;
    }
    

    AppRecordingStatus^ recordingStatus = m_appRecordingManager->GetStatus();
    
    Windows::Globalization::Calendar^ calendar = ref new Windows::Globalization::Calendar();
    calendar->SetToNow();

    Windows::Foundation::DateTime nowTime = calendar->GetDateTime();

    int secondsToRecord = min(30, historicalBufferDuration.Duration / 10000000);
    calendar->AddSeconds(-1 * secondsToRecord);

    Windows::Foundation::DateTime  startTime = calendar->GetDateTime();

    Windows::Foundation::TimeSpan duration;

    duration.Duration = nowTime.UniversalTime - startTime.UniversalTime;

    create_task(m_appRecordingManager->RecordTimeSpanToFileAsync(startTime, duration, file)).then(
        [this](AppRecordingResult^ result)
    {
        if (result->Succeeded)
        {
            Windows::Foundation::TimeSpan duration = result->Duration;
            boolean isTruncated = result->IsFileTruncated;
            UpdateStatusText("Recording completed.");
        }
        else
        {
            // If the recording failed, ExtendedError
            // can be retrieved and used for diagnostic purposes
            HResult extendedError = result->ExtendedError;
            LogTelemetryMessage("Error during recording: " + extendedError);
        }
    });

}

В примере ниже приведен базовый код для запуска операции записи из истории, показанной в предыдущем примере.The following example shows some basic code for initiating the historical record operation shown in the previous example.

StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
concurrency::create_task(storageFolder->CreateFileAsync("recordtimespantofile_example.mp4", CreationCollisionOption::ReplaceExisting)).then(
    [this](StorageFile^ file)
{
    RecordTimeSpanToFile(file);
});

Сохранение изображений снимков экрана в файлыSave screenshot images to files

Приложение может выполнить снимок экрана, который будет сохранять содержимое текущего окна приложения в один файл или несколько файлов изображений с различными кодировками изображений.Your app can initiate a screenshot capture that will save the current contents of the app's window to one image file or to multiple image files with different image encodings. Для указания кодировок изображения, которые вы хотите использовать, создайте список строковых переменных, где каждый элемент представляет тип изображения.To specify the image encodings you would like to use, create a list of strings where each represents an image type. Свойства объекта ImageEncodingSubtypes предоставляют правильное строковое значение для каждого типа поддерживаемого изображения, например MediaEncodingSubtypes.Png или MediaEncodingSubtypes.JpegXr.The properties of the ImageEncodingSubtypes provide the correct string for each supported image type, such as MediaEncodingSubtypes.Png or MediaEncodingSubtypes.JpegXr.

Инициируйте снимок экрана путем вызова метода SaveScreenshotToFilesAsync объекта AppRecordingManager.Initiate screen capture by calling the SaveScreenshotToFilesAsync method of the AppRecordingManager object. Первый параметр этого метода StorageFolder указывает папку сохранения файлов изображений.The first parameter to this method is a StorageFolder where the image files will be saved. Второй параметр — это префикс имени файла, к которому система добавит расширение, соответствующее типу сохраняемого изображения, например .png.The second parameter is a filename prefix to which the system will append the extension for each image type saved, such as ".png".

Третий параметр SaveScreenshotToFilesAsync необходим, чтобы система могла провести необходимое преобразование цветового пространства, если в текущем окне для захвата отображается HDR-содержимое.The third parameter to SaveScreenshotToFilesAsync is necessary for the system to be able to do the proper colorspace conversion if the current window to be captured is displaying HDR content. Если в кадре есть HDR-содержимое, этому параметру следует задать значение AppRecordingSaveScreenshotOption.HdrContentVisible.If HDR content is present, this parameter should be set to AppRecordingSaveScreenshotOption.HdrContentVisible. В противном случае используйте AppRecordingSaveScreenshotOption.None.Otherwise, use AppRecordingSaveScreenshotOption.None. Последний параметр метода — список форматов изображений, в которых следует сохранять снимок экрана.The final parameter to the method is the list of image formats to which the screen should be captured.

По завершении асинхронного вызова SaveScreenshotToFilesAsync возвращается объект AppRecordingSavedScreenshotInfo, который предоставляет StorageFile и соответствующее значение MediaEncodingSubtypes, указывающее тип изображения для каждого сохраненного изображения.When the asynchronous call to SaveScreenshotToFilesAsync completes, it returns a AppRecordingSavedScreenshotInfo object that provides the StorageFile and associated MediaEncodingSubtypes value indicating the image type for each saved image.

void App::SaveScreenShotToFiles(Windows::Storage::StorageFolder^ folder, Platform::String^ filenamePrefix)
{

    if (m_appRecordingManager == nullptr)
    {
        return;
    }


    Windows::Foundation::Collections::IVectorView<Platform::String^>^ supportedFormats = 
        m_appRecordingManager->SupportedScreenshotMediaEncodingSubtypes;

    
    Platform::Collections::Vector<Platform::String^>^ requestedFormats = 
        ref new Platform::Collections::Vector<Platform::String^>();

    for (Platform::String^ format : requestedFormats)
    {
        if (format == Windows::Media::MediaProperties::MediaEncodingSubtypes::Png)
        {
            requestedFormats->Append(format);
        }
        else if (format == Windows::Media::MediaProperties::MediaEncodingSubtypes::JpegXr)
        {
            requestedFormats->Append(format);
        }
    }


    create_task(m_appRecordingManager->SaveScreenshotToFilesAsync(folder, filenamePrefix, AppRecordingSaveScreenshotOption::None,
        requestedFormats->GetView())).then(
            [this](AppRecordingSaveScreenshotResult^ result)
    {
        if (result->Succeeded)
        {
            Windows::Foundation::Collections::IVectorView<AppRecordingSavedScreenshotInfo^>^ returnedScreenshots = result->SavedScreenshotInfos;

            for (AppRecordingSavedScreenshotInfo^ screenshotInfo : returnedScreenshots)
            {
                Windows::Storage::StorageFile^ file = screenshotInfo->File;
                Platform::String^ type = screenshotInfo->MediaEncodingSubtype;
            }
        }
        else
        {
            // If the recording failed, ExtendedError 
            // can be retrieved and used for diagnostic purposes 
            HResult extendedError = result->ExtendedError;
            LogTelemetryMessage("Error during screenshot: " + extendedError);
        }
    });
}

В примере ниже приведен базовый код для запуска операции снимка экрана, показанной в предыдущем примере.The following example shows some basic code for initiating the screenshot operation shown in the previous example.

StorageFolder^ storageFolder = ApplicationData::Current->LocalFolder;
SaveScreenShotToFiles(storageFolder, "screen_capture");

Добавление игровых метаданных для системных и инициированных приложением записейAdd game metadata for system and app-initiated capture

В следующих разделах этой статьи рассматриваются способы предоставления метаданных, которые система будет добавлять в записываемый или транслируемый поток MP4 для игрового процесса.The following sections of this article describe how to provide metadata that the system will embed into the MP4 stream of captured or broadcast gameplay. Метаданные могут быть добавлены в мультимедийное содержимое, записываемое с помощью встроенного системного пользовательского интерфейса либо средствами приложения с помощью AppRecordingManager.Metadata can be embedded in media that is captured using the built-in system UI and media that is captured by the app with AppRecordingManager. Эти метаданные могут быть извлечены вашими и другими приложениями во время воспроизведения мультимедиа, чтобы предоставлять соответствующую контексту информацию, синхронизируемую с записанным или транслируемым игровым процессом.This metadata can be extracted by your app and other apps during media playback in order to provide contextually-aware experiences that are synchronized with the captured or broadcast gameplay.

Получение экземпляра AppCaptureMetadataWriterGet an instance of AppCaptureMetadataWriter

Основной класс для управления записанными метаданными приложения — AppCaptureMetadataWriter.The primary class for managing app capture metadata is AppCaptureMetadataWriter. Перед инициализацией экземпляра этого класса используйте метод ApiInformation.IsApiContractPresent для запроса Windows.Media.Capture.AppCaptureMetadataContract версии 1.0, чтобы убедиться, что этот API доступен на данном устройстве.Before initializing an instance of this class, use the ApiInformation.IsApiContractPresent method to query for the Windows.Media.Capture.AppCaptureMetadataContract version 1.0 to verify that the API is available on the current device.

if (Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Media.Capture.AppCaptureMetadataContract", 1, 0))
{
    m_appCaptureMetadataWriter = ref new AppCaptureMetadataWriter();
}

Запись метаданных в кэш системы для вашего приложенияWrite metadata to the system cache for your app

Каждому элементу метаданных задается текстовая метка, идентифицирующая этот элемент, а также соответствующее значение, которое может быть строковым, целочисленным или типа double, а также значение из перечисления AppCaptureMetadataPriority, указывающее относительный приоритет этого элемента данных.Each metadata item has a string label, identifying the metadata item, an associated data value which can be a string, an integer, or a double value, and a value from the AppCaptureMetadataPriority enumeration indicating the relative priority of the data item. Элемент метаданных можно рассматривать как «событие», которое происходит в определенный момент, или «состояние», сохраняющееся в течение периода времени.A metadata item can either be considered an "event", which occurs at a single point in time, or a "state" which maintains a value over a time window. Метаданные записываются в кэш памяти, выделенный вашему приложению системой и управляемый ей.Metadata is written to a memory cache that is allocated and managed for your app by the system. Система применяет ограничения по размеру кэша памяти метаданных и после достижения предельного значения будет удалять данные на основании приоритета, с которым каждый элемент метаданных был записан.The system enforces a size limit on the metadata memory cache and, when the limit is reached, will purge data based on the priority with which each metadata item was written. В следующем разделе этой статьи рассказывается, как управлять выделением памяти метаданных приложения.The next section of this article shows how to manage your app's metadata memory allocation.

Стандартное приложение может записывать некоторые метаданные в начале сеанса захвата для создания контекста для последующих данных.A typical app may choose to write some metadata at the beginning of the capture session to provide some context for the subsequent data. Для этого сценария рекомендуется использовать данные мгновенного «события».For this scenario it is recommended that you use instantaneous "event" data. В этом примере вызываются AddStringEvent, AddDoubleEvent и AddInt32Event для установки мгновенных значений для каждого типа данных.This example calls AddStringEvent, AddDoubleEvent, and AddInt32Event to set instantaneous values for each data type.

void App::StartSession(Platform::String^ sessionId, double averageFps, int resolutionWidth, int resolutionHeight)
{
    if (m_appCaptureMetadataWriter != nullptr)
    {
        m_appCaptureMetadataWriter->AddStringEvent("sessionId", sessionId, AppCaptureMetadataPriority::Informational);
        m_appCaptureMetadataWriter->AddDoubleEvent("averageFps", averageFps, AppCaptureMetadataPriority::Informational);
        m_appCaptureMetadataWriter->AddInt32Event("resolutionWidth", resolutionWidth, AppCaptureMetadataPriority::Informational);
        m_appCaptureMetadataWriter->AddInt32Event("resolutionHeight", resolutionHeight, AppCaptureMetadataPriority::Informational);
    }
}

Распространенным сценарием использования данных «состояния», сохраняющегося в течение периода времени, является отслеживание игровой карты, на которой находится игрок.A common scenario for using "state" data that persists over time is to track the game map that the player is currently within. В этом примере вызывается StartStringState для установки значения состояния.This example calls StartStringState to set the state value.

void App::StartMap(Platform::String^ mapName)
{
    m_appCaptureMetadataWriter->StartStringState("map", mapName, AppCaptureMetadataPriority::Important);
}

Вызовите метод StopState для записи того, что определенное состояние закончилось.Call StopState to record that a particular state has ended.

void App::EndMap(Platform::String^ mapName)
{
    m_appCaptureMetadataWriter->StopState("map");
}

Можно перезаписать состояние, установив новое значение с существующей меткой состояния.You can overwrite a state by setting a new value with an existing state label.

void App::LevelUp(int newLevel)
{
    m_appCaptureMetadataWriter->StartInt32State("currentLevel", newLevel, AppCaptureMetadataPriority::Important);
}

Все открытые в данный момент состояния можно прекратить, вызвав метод StopAllStates.You can end all currently open states by calling StopAllStates.

void App::RaceComplete()
{
    m_appCaptureMetadataWriter->StopAllStates();
}

Управление ограничением хранилища кэша метаданныхManage metadata cache storage limit

Метаданные, которые вы пишете с помощью AppCaptureMetadataWriter, кэшируются системой до записи в соответствующий мультимедийный поток.The metadata that you write with AppCaptureMetadataWriter is cached by the system until it is written to the associated media stream. Система определяет ограничения размера для кэша метаданных каждого приложения.The system defines a size limit for each app's metadata cache. После достижения предельного размера кэша система начнет очистку кэшированных метаданных.Once the cache size limit has been reached, the system will begin purging cached metadata. Система удалит метаданные, записанные с помощью аппкаптуреметадатаприорити. значение приоритета для сведений перед удалением метаданных с помощью аппкаптуреметадатаприорити. Важно .The system will delete metadata that was written with AppCaptureMetadataPriority.Informational priority value before deleting metadata with the AppCaptureMetadataPriority.Important priority.

В любой момент можно проверить размер свободного пространства в байтах для кэша метаданных вашего приложения, вызвав метод RemainingStorageBytesAvailable.At any point, you can check to see the number of bytes available in your app's metadata cache by calling RemainingStorageBytesAvailable. Вы можете задать собственное пороговое значение для своего приложения, после достижения которого можно, например сократить объем метаданных записываемых в кэш.You can choose to set your own app-defined threshold after which you can choose to reduce the amount of metadata that you write to the cache. В следующем примере показана простая реализация такого подхода.The following example shows a simple implementation of this pattern.

void App::CheckMetadataStorage()
{
    INT64 storageRemaining = m_appCaptureMetadataWriter->RemainingStorageBytesAvailable;

    if (storageRemaining < m_myLowStorageLevelInBytes)
    {
        m_writeLowPriorityMetadata = false;
    }
}
void App::ComboExecuted(Platform::String^ comboName)
{
    if (m_writeLowPriorityMetadata)
    {
        m_appCaptureMetadataWriter->AddStringEvent("combo", comboName, AppCaptureMetadataPriority::Informational);
    }
}

Получение уведомлений об очистке метаданных системойReceive notifications when the system purges metadata

Вы можете зарегистрироваться, чтобы получить уведомление при запуске системой очистки метаданных для приложения путем регистрации обработчика для события метадатапуржед .You can register to receive a notification when the system begins purging metadata for your app by registering a handler for the MetadataPurged event.

if (m_appCaptureMetadataWriter != nullptr)
{
    m_appCaptureMetadataWriter->MetadataPurged += 
        ref new TypedEventHandler<AppCaptureMetadataWriter^, Platform::Object^>(this, &App::OnMetadataPurged);

}

В обработчике для события MetadataPurged можно освободить дополнительное пространство в кэше метаданных, завершив состояния с более низким приоритетом, можно реализовать определяемую приложением логику сокращения объема метаданных, записываемых в кэш, либо можно не выполнять никаких действий и разрешить системе продолжить очистку кэша в зависимости от приоритета, с которым данные были записаны.In the handler for the MetadataPurged event, you can clear up some room in the metadata cache by ending lower-priority states, you can implement app-defined logic for reducing the amount of metadata you write to the cache, or you can do nothing and let the system continue to purge the cache based on the priority with which it was written.

void App::OnMetadataPurged(Windows::Media::Capture::AppCaptureMetadataWriter^ sender, Platform::Object^ args)
{
    // Reduce metadata by stopping a low-priority state.
    //m_appCaptureMetadataWriter->StopState("map");

    // Reduce metadata by stopping all states.
    //m_appCaptureMetadataWriter->StopAllStates();

    // Change app-specific behavior to write less metadata.
    //m_writeLowPriorityMetadata = false;

    // Take no action. Let the system purge data as needed. Record event for telemetry.
    OutputDebugString(TEXT("Low-priority metadata purged."));

}