Запись звука, видео, снимков экрана и метаданных из игры

В этой статье рассказывается, как выполнять запись видео, звука и снимков экрана в играх, а также как отправлять метаданные, которые система добавит в записанное и транслируемое мультимедийное содержимое, позволяя вашим и другим приложениям обеспечивать динамичное взаимодействие, синхронизируемое с событиями игрового процесса.

Существует два разных способа записи процесса игры в приложении UWP. Пользователь может запустить запись, используя встроенный системный пользовательский интерфейс. Мультимедиа, захваченные с помощью этой методики, подается в игровую экосистему Майкрософт, их можно просматривать и делиться ими с помощью сторонних интерфейсов, таких как приложение Xbox, и они недоступны напрямую для вашего приложения или пользователей. В начальных разделах этой статьи рассказывается, как включать и отключать реализованные в системе средства записи работы приложения и как получать уведомления, когда запись запускается или останавливается.

Другой способ записи мультимедийного содержимого — использовать API-интерфейсы пространства имен Windows.Media.AppRecording. Если на устройстве включена возможность записи, ваше приложение можно начать запись игрового процесса, и затем по прошествии некоторого времени вы можете остановить запись, после чего мультимедийное содержимое записывается в файл. Если пользователь включил возможность записи из истории, можно также записывать уже прошедший игровой процесс, указав время начала записи в прошлом и ее длительность. Оба этих метода создают видеофайл, доступный вашему приложению, а также, в зависимости от того, где вы решите сохранять файлы, пользователю. В середине этой статьи подробно рассматривается реализация этих сценариев.

Пространство имен Windows.Media.Capture предоставляет API-интерфейсы для создания метаданных, описывающих записываемый или транслируемый игровой процесс. Сюда могут включаться текстовые и числовые значения с текстовыми метками для обозначения каждого элемента данных. Метаданные могут представлять «событие», которое происходит в определенный момент, например когда пользователь заканчивает круг в гонке, либо «состояние», которое сохраняется некоторое время, например игровая карта, на которой находится игрок. Метаданные записываются в кэш, выделенный вашему приложению системой и управляемый ей. Метаданные добавляются в трансляции и записанные видеофайлы, создаваемые как с помощью встроенных системных средств записи, так и собственных средств записи в приложениях. В заключительных разделах этой статьи рассказывается, как записывать метаданные игрового процесса.

Примечание

Поскольку метаданные игрового процесса могут добавляться в мультимедиа-файлы, которые могут передаваться по сети вне контроля пользователя, не следует включать личные сведения и другую потенциально закрытую информацию в метаданные.

Включение и отключение системных средств записи работы приложения

Системная запись работы приложения запускается пользователем с помощью встроенного пользовательского интерфейса. Эти файлы помечаются игровой экосистемой Windows и недоступны вашему приложению или пользователю, за исключением таких интерфейсов, как приложение Xbox. Ваше приложение может отключить и включить инициируемую системой запись, что позволяет запретить пользователям записывать определенное содержимое или игровой процесс.

Чтобы включить или отключить системную запись приложения, просто вызовите статический метод AppCapture.SetAllowedAsync и передайте false для отключения записи или true для ее включения.

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

Получение уведомлений при запуске и остановке системной записи работы приложения

Чтобы получать уведомления, когда системная запись приложения начинается или заканчивается, необходимо сначала получить экземпляр класса AppCapture, вызвав фабричный метод GetForCurrentView. Затем зарегистрируйте обработчик события CapturingChanged.

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, чтобы определить, ведется ли запись звука и видео, соответственно. Возможно, потребуется обновление пользовательского интерфейса приложения для отражения текущего состояния записи.

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 в приложение

API-интерфейсы для записи звука и видео и захвата снимков экрана непосредственно из приложения, предлагаемые в пространстве имен Windows.Media.AppRecording, не включаются в универсальный контракт API. Для доступа к этим API необходимо добавить ссылку на расширения рабочего стола Windows для UWP в приложение, выполнив следующие действия.

  1. В Visual Studio в обозревателе решений разверните свой проект UWP и щелкните правой кнопкой мыши папку Ссылки, а затем выберите пункт Добавить ссылку....
  2. Разверните узел универсальных приложений для Windows и выберите Расширения.
  3. В списке расширений установите флажок рядом с пунктом расширения рабочего стола Windows для UWP, который соответствует целевой сборке для проекта. Для функций трансляции приложения версия должна быть 1709 или более поздней.
  4. Нажмите кнопку ОК.

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

Класс AppRecordingManager является центральным API, который вы будете использовать для управления записью в приложении. Получите экземпляр этого класса, вызвав фабричный метод GetDefault. Прежде чем использовать какие-либо API-интерфейсы в пространстве имен Windows.Media.AppRecording, следует проверять их наличие на текущем устройстве. API-интерфейсы недоступны на устройствах под управлением версий ОС более ранних, чем Windows 10, версия 1709. Вместо того чтобы проверить наличие определенной версии операционной системы, используйте метод ApiInformation.IsApiContractPresent для запроса Windows.Media.AppBroadcasting.AppRecordingContract версии 1.0. Если этот контракт представлен, API-интерфейсы записи доступны на этом устройстве. В примере кода в этой статье сначала проверяется наличие API, а затем проверяется, имеет ли AppRecordingManager значение null, перед выполнением последующих операций.

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

Определение возможности записи вашего приложения

Существует несколько причин, по которым приложение может в определенный момент не иметь возможности записывать звук или видео; в их число входит несоответствие устройства требованиям к оборудованию для записи и осуществление трансляции другим приложением. Перед началом записи можно проверить, имеет ли ваше приложение возможность записи в данный момент. Вызовите метод GetStatus объекта AppRecordingManager, а затем проверьте свойство CanRecord возвращенного объекта AppRecordingStatus. Если функция CanRecord возвращает значение false, то есть приложение не может записывать данные в данный момент, можно проверка свойство Details, чтобы определить причину. В зависимости от причины, возможно, вы решите отобразить состояние пользователю или показать инструкции для включения записи в приложении.

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;
}

Запуск и остановка записи работы приложения в файл вручную

Убедившись, что ваше приложение может вести запись, можно начать новую запись, вызвав метод StartRecordingToFileAsync объекта AppRecordingManager.

В следующем примере первый блок then выполняется в случае сбоя асинхронной задачи. Второй блок then пытается получить доступ к результату задачи, и, если результат равен null, значит, задача завершена. В обоих случаях для обработки результата вызывается вспомогательный метод OnRecordingComplete, показанный ниже.

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, чтобы определить успешно ли прошла операция записи. Если успешно, вы можете проверить свойство IsFileTruncated, чтобы определить, не пришлось ли системе принудительно обрезать записанный файл в связи с нехваткой пространства. Вы можете проверить свойство Duration для определения фактической продолжительности записанного файла, если файл обрезан, его длительность может быть меньше продолжительности операции записи.

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;
    }
}

Ниже приведены примеры базового кода для запуска и остановки операции записи, показанной в предыдущем примере.

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();
}

Запись исторического отрезка в файл

Если пользователь включил возможность записи исторических отрезков для вашего приложения в параметрах системы, вы можете записать отрезок игрового процесса, который проходил ранее. В предыдущем примере в этой статье показано, как убедиться, что ваше приложение в данный момент может записывать игровой процесс. Существует дополнительная проверка для того, чтобы определить, включена ли возможность записи исторического отрезка. Снова вызовите GetStatus и проверьте свойство CanRecordTimeSpan возвращенного объекта AppRecordingStatus. В этом примере также возвращается свойство HistoricalBufferDuration объекта AppRecordingStatus, которое будет использоваться для определения допустимого времени запуска операции записи.

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;
}

Чтобы записать исторический отрезок, необходимо указать время начала записи и ее длительность. Время начала предоставляется в виде структуры DateTime. Время начала должно быть раньше текущего времени в пределах продолжительности буфера истории для записи. В этом примере длина буфера определяется в ходе проверки того, включена ли возможность записи из истории, как показано в предыдущем примере кода. Длительность записи журнала указывается в виде структуры TimeSpan , которая также должна быть равна или меньше длительности буфера журнала. После определения нужного времени и продолжительности вызовите RecordTimeSpanToFileAsync для запуска операции записи.

Как и в случае с ручным запуском и остановкой записи, по завершении записи из истории можно проверить свойство Succeeded возвращенного объекта AppRecordingResult, чтобы определить, завершилась ли операция записи успешно, а также можно проверить свойства IsFileTruncated и Duration для определения фактической продолжительности записанного файла, которая может быть меньше заданной продолжительности, если файл обрезан.

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);
        }
    });

}

В примере ниже приведен базовый код для запуска операции записи из истории, показанной в предыдущем примере.

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

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

Приложение может выполнить снимок экрана, который будет сохранять содержимое текущего окна приложения в один файл или несколько файлов изображений с различными кодировками изображений. Для указания кодировок изображения, которые вы хотите использовать, создайте список строковых переменных, где каждый элемент представляет тип изображения. Свойства объекта ImageEncodingSubtypes предоставляют правильное строковое значение для каждого типа поддерживаемого изображения, например MediaEncodingSubtypes.Png или MediaEncodingSubtypes.JpegXr.

Инициируйте снимок экрана путем вызова метода SaveScreenshotToFilesAsync объекта AppRecordingManager. Первый параметр этого метода StorageFolder указывает папку сохранения файлов изображений. Второй параметр — это префикс имени файла, к которому система добавит расширение, соответствующее типу сохраняемого изображения, например .png.

Третий параметр SaveScreenshotToFilesAsync необходим, чтобы система могла провести необходимое преобразование цветового пространства, если в текущем окне для захвата отображается HDR-содержимое. Если в кадре есть HDR-содержимое, этому параметру следует задать значение AppRecordingSaveScreenshotOption.HdrContentVisible. В противном случае используйте AppRecordingSaveScreenshotOption.None. Последний параметр метода — список форматов изображений, в которых следует сохранять снимок экрана.

По завершении асинхронного вызова SaveScreenshotToFilesAsync возвращается объект AppRecordingSavedScreenshotInfo, который предоставляет StorageFile и соответствующее значение MediaEncodingSubtypes, указывающее тип изображения для каждого сохраненного изображения.

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);
        }
    });
}

В примере ниже приведен базовый код для запуска операции снимка экрана, показанной в предыдущем примере.

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

Добавление игровых метаданных для системных и инициированных приложением записей

В следующих разделах этой статьи рассматриваются способы предоставления метаданных, которые система будет добавлять в записываемый или транслируемый поток MP4 для игрового процесса. Метаданные могут быть добавлены в мультимедийное содержимое, записываемое с помощью встроенного системного пользовательского интерфейса либо средствами приложения с помощью AppRecordingManager. Эти метаданные могут быть извлечены вашими и другими приложениями во время воспроизведения мультимедиа, чтобы предоставлять соответствующую контексту информацию, синхронизируемую с записанным или транслируемым игровым процессом.

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

Основной класс для управления записанными метаданными приложения — AppCaptureMetadataWriter. Перед инициализацией экземпляра этого класса используйте метод ApiInformation.IsApiContractPresent для запроса Windows.Media.Capture.AppCaptureMetadataContract версии 1.0, чтобы убедиться, что этот API доступен на данном устройстве.

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

Запись метаданных в кэш системы для вашего приложения

Каждому элементу метаданных задается текстовая метка, идентифицирующая этот элемент, а также соответствующее значение, которое может быть строковым, целочисленным или типа double, а также значение из перечисления AppCaptureMetadataPriority, указывающее относительный приоритет этого элемента данных. Элемент метаданных можно рассматривать как «событие», которое происходит в определенный момент, или «состояние», сохраняющееся в течение периода времени. Метаданные записываются в кэш памяти, выделенный вашему приложению системой и управляемый ей. Система применяет ограничения по размеру кэша памяти метаданных и после достижения предельного значения будет удалять данные на основании приоритета, с которым каждый элемент метаданных был записан. В следующем разделе этой статьи рассказывается, как управлять выделением памяти метаданных приложения.

Стандартное приложение может записывать некоторые метаданные в начале сеанса захвата для создания контекста для последующих данных. Для этого сценария рекомендуется использовать данные мгновенного «события». В этом примере вызываются AddStringEvent, AddDoubleEvent и AddInt32Event для установки мгновенных значений для каждого типа данных.

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);
    }
}

Распространенным сценарием использования данных «состояния», сохраняющегося в течение периода времени, является отслеживание игровой карты, на которой находится игрок. В этом примере вызывается StartStringState для установки значения состояния.

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

Вызовите метод StopState для записи того, что определенное состояние закончилось.

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

Можно перезаписать состояние, установив новое значение с существующей меткой состояния.

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

Все открытые в данный момент состояния можно прекратить, вызвав метод StopAllStates.

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

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

Метаданные, которые вы пишете с помощью AppCaptureMetadataWriter, кэшируются системой до записи в соответствующий мультимедийный поток. Система определяет ограничения размера для кэша метаданных каждого приложения. После достижения предельного размера кэша система начнет очистку кэшированных метаданных. Система удалит метаданные, записанные с помощью значения Приоритета AppCaptureMetadataPriority.Информационный , прежде чем удалять метаданные с приоритетом AppCaptureMetadataPriority.Important .

В любой момент можно проверить размер свободного пространства в байтах для кэша метаданных вашего приложения, вызвав метод RemainingStorageBytesAvailable. Вы можете задать собственное пороговое значение для своего приложения, после достижения которого можно, например сократить объем метаданных записываемых в кэш. В следующем примере показана простая реализация такого подхода.

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);
    }
}

Получение уведомлений об очистке метаданных системой

Вы можете зарегистрироваться для получения уведомления, когда система начнет очистку метаданных вашего приложения, зарегистрировав обработчик для события MetadataPurged .

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

}

В обработчике для события MetadataPurged можно освободить дополнительное пространство в кэше метаданных, завершив состояния с более низким приоритетом, можно реализовать определяемую приложением логику сокращения объема метаданных, записываемых в кэш, либо можно не выполнять никаких действий и разрешить системе продолжить очистку кэша в зависимости от приоритета, с которым данные были записаны.

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."));

}