Пользовательский интерфейс приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript

Списки воспроизведения

Разбить на страницы
Показывать лекцию целиком

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

WinRT поддерживает эти действия посредством простого API в пространстве имен ), используя форматы WPL (Windows Media Player), ZPL (Zune) и M3U. Пример "Список воспроизведения" (http://code.msdn.microsoft.com/windowsapps/Playlist-sample-3d80daee) в Windows SDK (который почти победил в конкурсе на самое короткое название примера!) показывает, как выполнять различные задачи с данным API. В Сценарии 1 он позволяет вам выбрать несколько файлов с использованием средства выбора файлов, создает новый объект Windows. ), добавляет выбранные файлы в его список files (вектор объектов StorageFile) и сохраняет список воспроизведения с помощью метода saveAsAsync (этот код из create.js упрощен и слегка переформатирован):

 
function pickAudio() {	
var picker = new Windows.Storage.Pickers.FileOpenPicker();	
picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.musicLibrary;
picker.fileTypeFilter.replaceAll(SdkSample.audioExtensions);	

picker.pickMultipleFilesAsync().done(function (files) {
if (files.size > 0) {
SdkSample.playlist = new Windows.Media.Playlists.Playlist();

files.forEach(function (file) { SdkSample.playlist.files.append(file);
});

SdkSample.playlist.saveAsAsync(Windows.Storage.KnownFolders.musicLibrary,
"Sample", Windows.Storage.NameCollisionOption.replaceExisting,	
Windows.Media.Playlists.PlaylistFormat.windowsMedia)	
.done();	
}	
}	

Обратите внимание на то, что saveAsAsync принимает объект типа StorageFolder и имя для файла (вместе с необязательным параметром формата). Это соответствует обычно используемому шаблону для списков воспроизведения, где у музыкального приложения есть отдельная папка, где оно хранит списки воспроизведения и предоставляет пользователю простые средства для их именования и/или выбора. При таком подходе списками воспроизведения обычно управляют не так как пользовательскими файлами, когда действия выполняются с помощью средства выбора файлов для выполнения команды Сохранить как (Save as) в произвольной папке. Вы можете использовать FileSavePicker, получить StorageFile, и использовать его свойство path для получения подходящего объекта StorageFolder, но вероятнее всего, вы будете сохранять списки воспроизведения в одном месте и представлять их в виде сущностей, которые появляются лишь внутри самого приложения.

Например, приложение Музыка (Music), поставляемое вместе с Windows 8, позволяет вам создавать новый список воспроизведения, когда вы просматриваете записи некоторого альбома. Следующие команды появляются на панели приложения (слева), и когда вы выбираете команду Новый список воспроизведения (New playlist), появляется всплывающий элемент (в середине), запрашивающий имя, после чего всплывающий элемент появляется на панели приложения (справа):

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

Для загрузки плейлиста используется метод Playlist.loadAsync, которому передается StorageFile для плейлиста. Это может быть объект StorageFile, полученный от средства выбора файлов, или из перечисления выделенной для списков воспроизведения папки приложения. Сценарий 2 примера "Список воспроизведения" (display.js) показывает последний подход, после чего осуществляется обход файлов и запрос их музыкальных свойств:


function displayPlaylist() {	
var picker = new Windows.Storage.Pickers.FileOpenPicker();	
picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.musicLibrary;
picker.fileTypeFilter.replaceAll(SdkSample.playlistExtensions);	

var promiseCount = 0;

picker.pickSingleFileAsync()
.then(function (item) {
if (item) {
return Windows.Media.Playlists.Playlist.loadAsync(item);
}
return WinJS.Promise.wrapError("No file picked.");
})
.then(function (playlist) { SdkSample.playlist = playlist;
var promises = {};

// Запрос музыкальных свойств для каждого файла в списке.	
playlist.files.forEach(function (file) {	
promises[promiseCount++] = file.properties.getMusicPropertiesAsync();
});	

// Вывод музыкальных свойств каждого файла. По причине асинхронной
// природы вызова по получению музыкальных свойств, данные могут появиться
// в ином порядке, нежели они заданы в исходном списке воспроизведения.
// Для обеспечения верного порядка, мы используем Promise.join с ассоциативным массивом
// переданным в качестве параметра, содержащим индекс каждого отдельного promise.
return WinJS.Promise.join(promises);	
})	
.done(function (results) {	
var output = "Playlist content:\n\n";	

var musicProperties;	
for (var resultIndex = 0; resultIndex < promiseCount; resultIndex++) {
musicProperties = results[resultIndex];	
output += "Title: " + musicProperties.title + "\n";	
output += "Album: " + musicProperties.album + "\n";	
output += "Artist: " + musicProperties.artist + "\n\n";	
}	

if (resultIndex === 0) {
output += "(playlist is empty)";
}

}, function (error) {
// ...
});
}
 

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

Другой метод управления плейлистами – это PlayList.saveAsync, который принимает единственный StorageFile. Такой подход используется, когда вы загружаете и модифицируете список воспроизведения и просто хотите сохранить эти изменения (что обычно выполняется автоматически, когда пользователь добавляет или удаляет элементы плейлиста). Это показано в Сценариях 3, 4 и 5 примера (add.js, js/remove.js и js/clear.js), где просто использованы методы вектора Playlist.files, наподобие append, removeAtEnd и clear, соответственно.

Проигрывание списка воспроизведения, конечно, зависит от типов включенных в него элементов, но обычно загружают плейлист и последовательно получают объекты StorageFile из его вектора files, передают их в URL.createObjectURL, и затем присваивают полученный URI атрибуту src элемента audio или video. Вы можете, кроме того, использовать плейлисты для управления списками изображений для слайд-шоу

Загрузка мультимедийных данных и управление ими

Пользователь может хранить медиафайлы везде, но изображения, музыка и видео обычно хранятся в соответствующих библиотеках пользователя. Проще говоря, это папки, которые медиа-приложениям следует использовать по умолчанию до тех пор, пока пользователь не укажет иную папку с помощью средства выбора папок. Как мы видели в лекции 2, приложения могут объявлять о программном доступе к библиотекам изображений, музыки и видео в манифестах, и получать объекты StorageFolder для них посредством Windows.Storage.KnownFolders:

var picsLib = Windows.Storage.KnownFolders.picturesLibrary;
 var musicLib = Windows.Storage.KnownFolders.musicLibrary;
 var vidsLib = Windows.Storage.KnownFolders.videosLibrary;
 

Приложения для работы с фотографиями обычно объявляют возможность Библиотека изображений (Pictures Library), и отображают ее содержимое в ListView. Приложения для работы с музыкой и видео так же используют соответствующие библиотеки, как вы можете видеть во встроенных приложениях Windows 8 Фотографии (Photos), Музыка (Music) и Видео (Video). Помните так же, что если вы забудете объявить соответствующие возможности, строки кода выше приведут к выдаче исключения "Доступ запрещен". Вы тут же поймете, если вы забудете эти важные особенности.

Я должен предупредить вас заранее, что работа с мультимедиа может быть очень сложной и запутанной. Поэтому вы, возможно, сочтете полезным просмотреть несколько материалов в документации, таких, как "Обработка файлов изображений" (http://msdn.microsoft.com/library/windows/apps/hh465103.aspx), "Перекодировка" (http://msdn.microsoft.com/ru-ru/library/windows/apps/hh452806.aspx), "Применение расширений мультимедиа" (http://msdn.microsoft.com/library/windows/apps/Hh700365.aspx).

Метаданные мультимедийных файлов

Когда у нас есть StorageFolder для некоторой медиабиблиотеки или некоторый набор таких объектов, мы можем использовать, как мы уже видели в лекции 2, их метод getItemAsync для получения их содержимого. Так же вы можете использовать файловые запросы для перечисления файлов, которые подпадают под некоторый критерий. В любом случае, в итоге вы получаете коллекцию объектов storageFile, с которой вы можете работать так, как вам нужно.

Теперь начинается кое-что интересное. Как я упоминал в лекции 2, вы можете получать дополнительные метаданные для этих файлов. Имеется несколько уровней данных, которые вы будете исследовать, начиная открывать тайные двери класса StorageFile, как показано на рис. 6.1. Следующий раздел, в свою очередь, посвящем этим областям. (рис 6.1) Взаимосвязь между объектом StorageFile и другими объектами, получаемыми с его помощью

Эскизы

Во-первых, вызов StorageFile.getThumbnailAsync предоставляет изображение эскиза, соответствующее конкретному "режиму" из перечисления ). Здесь есть такие варианты, как ), который предоставляет данные эскиза в виде потока. Вы можете удобно передать его нашему старому другу URL.createObjectURL для отображения в элементе img и так далее.

Примеры этого найти в примере "Эскизы файлов и папок" (http://code.msdn.microsoft.com/windowsapps/File-thumbnails-sample-17575959). Сценарий 1, например, (js/scenario1.js), предоставляет эскиз и отображает его в элементе img:

file.getThumbnailAsync(thumbnailMode, requestedSize, thumbnailOptions).done(function (thumbnail) {
if (thumbnail) {	
outputResult(file, thumbnail, modeNames[modeSelected], requestedSize);	
}	
// ...	
});	

function outputResult(item, thumbnailImage, thumbnailMode, requestedSize) {	
document.getElementById("picture-thumb-imageHolder").src = URL.createObjectURL(thumbnailImage,
{ oneTimeOnly: true });	
// ...	
}	
 

Общие свойства файлов

Общие свойства файлов – это те свойства, которые имеются у всех файлов – находятся во множестве различных мест. Наиболее общие свойства можно обнаружить непосредственно в объекте StorageFile, наподобие attributes, contentType, dateCreated, displayName, displayType, fileType, name, и path.

Свойства следующей группы можно получить из ), который содержит свойства dateModified, itemDate, и size. "Вот скукота!", скажете вы себе. Ну хорошо, у этого объекта еще есть дополнительный метод, который называется retrievePropertiesAsync, который предоставляет вам массив пар имя – значение для разных штуковин.

Весь фокус в том, чтобы понять, что вы можете взять массив имен свойств, которые вам нужны, где каждое имя – это строка, происходящая из очень длинного списка свойств Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977.aspx), таких, как System.FileOwner и System.FileAttributes, и передать его ), который мы видели в лекции 2:

 var dateAccessedProperty = "System.DateAccessed";
var fileOwnerProperty   = "System.FileOwner";

SdkSample.sampleFile.getBasicPropertiesAsync().then(function (basicProperties) { 
outputDiv.innerHTML += "Size: " + basicProperties.size + " bytes<br />"; 
outputDiv.innerHTML += "Date modified: " + basicProperties.dateModified + "<br />";

// Получить дополнительные свойства
return SdkSample.sampleFile.properties.retrievePropertiesAsync([fileOwnerProperty, dateAccessedProperty]);
}).done(function (extraProperties) {
var propValue = extraProperties[dateAccessedProperty];
if (propValue !== null) {
outputDiv.innerHTML += "Date accessed: " + propValue + "<br />";
}
propValue = extraProperties[fileOwnerProperty];
if (propValue !== null) {
outputDiv.innerHTML += "File owner: " + propValue;
}
});

Что здесь особенно удобно, так это то, что здесь вы можете получить почти любое свойство, которое вам нужно (список свойств содержит тысячи позиций), затем модифицировать массив и вызвать BasicProperties.savePropertiesAsync. Вуаля! Только что вы обновили эти свойства файлов. Вариация savePropertiesAsync так же позволяет вам передавать специальный массив из пар имя – значение, если вы лишь хотите изменить лишь некоторые из свойств.

Третий набор свойств можно найти, если пройти через тайную дверь ), методы которого retrievePropertiesAsync и savePropertiesAsync похожи на те, что мы видели у BasicProperties. Гораздо интереснее то, что у него есть еще четыре метода: getDocumentPropertiesAsync, getImagePropertiesAsync, getMusicPropertiesAsync, и getVideoPropertiesAsync. С их помощью вы можете получить по-настоящему специальные сведения для конкретных типов файлов, как мы увидим дальше.

Специальные свойства мультимедиа

Вместе с классом BasicProperties в пространстве имен Windows.Storage.FileProperties мы так же можем найти классы, возвращаемые методами StorageFile.properties.get*PropertiesAsync: ImageProperties, VideoProperties, MusicProperties, и DocumentProperties. Хотя мы и так довольно далеко забрались, чтобы их найти, каждый из них, по-настоящему глубоко, содержит несметные информационные сокровища. Таблицы ниже, в свою очередь, приводят сводные данные по ним. Обратите внимание на то, что объект каждого типа содержит метод retrievePropertiesAsync, похожий на соответствующий метод BasicProperties, который позволяет вам запрашивать дополнительные свойства по имени, которые не включены в свойства основного объекта. Воспользуйтесь ссылками в верхней части таблицы для того, чтобы воспользоваться справочными разделами, которые позвлят обнаружить наиболее подходящие свойства Windows.

ImageProperties из StorageFile.properties.getImagePropertiesAsync
Дополнительные свойства System.Image, System.Photo, System.Media
Свойство Тип данных Соответствующее свойство Windows
title String System.Title
dateTaken Date System.Photo.DateTaken
latitude Double (смотрите ниже) System.GPS.LatitudeDecimal, мли комбинация System.GPS.Latitude, System.GPS.LatitudeDenominator, System.GPS.LatitudeNumerator, and System.GPS.LatitudeRef
longitude Double (смотрите ниже) System.GPS.LongitudeDecimal, или комбинация System.GPS.Longitude, System.GPS.LongitudeDenominator, System.GPS.LongitudeNumerator, and System.GPS.LongitudeRef
cameraManufacturer String System.Photo.CameraManufacturer
cameraModel String System.Photo.CameraModel
width Количество в пикселях System.Image.HorizontalSize
height Количество в пикселях System.Image.VerticalSize
orientation Windows.Storage.FileProperties.- PhotoOrientation содержит unspecified, normal, flipHorizontal, flipVertical, transpose, transverse, rotate90, rotate180, rotate270 System.Photo.Orientation
peopleNames String vector System.Photo.PeopleNames
keywords String vector System.Keywords
rating Число (1-99 с 0 означающим "нет оценки") System.Rating
VideoProperties из StorageFile.properties.getVideoPropertiesAsync
Дополнительные свойства System.Video, System.Media, System.Image, System.Photo
Свойство Тип данных Соответствующее свойство Windows
title String System.Title
subtitle String System.Media.SubTitle
year Number System.Media.Year
publisher String System.Media.Publisher
rating Number System.Rating
width Number in pixels System.Video.FrameWidth
height Number in pixels System.Video.FrameHeight
orientation Windows.Storage.FileProperties.- VideoOrientation содержит normal, rotate90, rotate180, rotate270 System.Photo.Orientation
duration Число (в 100-нс единицах, т.е. 1/10th миллисекунды) System.Media.Duration
bitrate Число (в битах в секунду) System.Video.TotalBitrate, System.Video.EncodingBitrate
directors String vector System.Video.Director
producers String vector System.Media.Producer
writers String vector System.Media.Writer
keywords String vector System.Keywords
latitude Double (смотрите ниже) System.GPS.LatitudeDecimal, или комбинация System.GPS.Latitude, System.GPS.LatitudeDenominator, System.GPS.LatitudeNumerator, and System.GPS.LatitudeRef
longitude Double (смотрите ниже) System.GPS.LongitudeDecimal, или комбинация System.GPS.Longitude, System.GPS.LongitudeDenominator, System.GPS.LongitudeNumerator, and System.GPS.LongitudeRef
MusicProperties из StorageFile.properties.getMusicPropertiesAsync
Дополнительные свойства System.Music, System.Media
Свойство Тип данных Соответствующее свойство Windows
title String System.Title, System.Music.AlbumTitle
subtitle String System.Media.SubTitle
trackNumber Number System.Music.TrackNumber
year Number System.Media.Year
publisher String System.Media.Publisher
artist String System.Music.Artist, System.Music.DisplayArtist
albumArtist String System.Music.DisplayArtist (чтение), System.Music.AlbumArtist (запись)
genre String vector System.Music.Genre
composers String vector System.Music.Composer
conductors String vector System.Music.Conductor
rating Число (1-99 с 0 означающим "нет оценки") System.Rating
duration Число (в 100-нс единицах, т.е. 1/10th миллисекунды System.Media.Duration
bitrate Число (в битах в секунду) System.Video.TotalBitrate, System.Video.EncodingBitrate
producers String vector System.Media.Producer
writers String vector System.Media.Writer
DocumentProperties from
Дополнительные свойства System
Свойство Тип данных Соответствующее свойство Windows
title String System.Title
Author String vector System.Author
keywords String vector System.Keywords
Comments String System.Comment

Пара замечаний обо всем этом. Во-первых, переменные String vector (строковые векторы) – это, как мы видели ранее, экземпляры ), который предоставляет методы для манипуляции, наподобие append, insertAt, removeA, и так далее. в JavaScript вы можете получить доступ к членам вектора как к массиву, через квадратные скобки [ ]; следует лишь помнить, что доступные методы более специфичны.

Во-вторых, свойства ) (в js/default.js) содержит вспомогательную функцию для извлечения компонентов этих значений и конвертирования их в строку:

"convertLatLongToString": function (latLong, isLatitude) {
var reference;

if (isLatitude) {	
reference = (latLong >= 0) ? "N" : "S";
} else {	
reference = (latLong >= 0) ? "E" : "W";
}	

latLong = Math.abs(latLong);	
var degrees = Math.floor(latLong);	
var minutes = Math.floor((latLong - degrees) * 60);	
var seconds = ((latLong - degrees - minutes / 60) * 3600).toFixed(2);
return degrees + "°" + minutes + "\'" + seconds + "\"" + reference;
}
 

Подводя итоги, знак величины указывает на направление. Положительное значение для широты (latitude) указывает на Север, отрицательное означает Юг, для долготы (longitude) положительное значение означает Восток, отрицательное – Запад. Целая часть числа представляет собой градусы, дробная содержит значение минут, за базу взято 60. Умножение этого значения на 60 дает полное количество минут, то, что остается, содержит секунды. Это может показаться странным, но подобные исходные данные получают от GPS-приемника и API определения местоположения обычно конвертирует их.

Мультимедийные свойства в примерах

Несколько примеров из Windows SDK показывают, как работать с некоторыми из вышеописанных свойств и как работать со свойствами в общем. Пример "Простая работа с изображениями" (http://code.msdn.microsoft.com/windowsapps/Simple-Imaging-Sample-a2dec2b0), в Сценарии 1, предоставляет наиболее полную демонстрацию, так как вы можете выбрать файл изображения и он загрузит и отобразит различные свойства, как показано на рис. 6.2 (я прокрутил список вниз, чтобы показать полный набор свойств). Я могу подтвердить, что дата, производитель и модель камеры, сведения об экспозиции, верны. (рис 6.2) Свойства файла изображения в примере "Простая работа с изображениями"

Метод примера openHandler получает эти свойства из файла, в частности, выполняя вызов StorageFile.properties.getImagePropertiesAsync и используя метод ImageProperties.retrievePropertiesAsync для пары дополнительных свойств уже не в ImageProperties. Затем getImagePropertiesForDisplay объединяет их в единый объект, используемый для вывода данных в пользовательский интерфейс примера. Некоторые строки кода, приведенного ниже, опущены.

var ImageProperties = {};

function openHandler() {
// Хранит данные между асинхронными вызовами. 
var file = {};

Helpers.getFileFromOpenPickerAsync().then(function (_file) {
file = _file;	
return file.properties.getImagePropertiesAsync();	
}).then(function (imageProps) {	
ImageProperties = imageProps;	

var requests = [	
"System.Photo.ExposureTime",    // В секундах	
"System.Photo.FNumber"    // Значение диафрагмы из данных EXIF
};	

return ImageProperties.retrievePropertiesAsync(requests);	
}).done(function (retrievedProps) {	
// Форматирование свойств в виде текста для отображения в интерфейсе.	
displayImageUI(file, getImagePropertiesForDisplay(retrievedProps));
});	
 }	

function getImagePropertiesForDisplay(retrievedProps) {
// Если заданное свойство не существует, его значением будет null.
var orientationText = Helpers.getOrientationString(ImageProperties.orientation);

var exposureText = retrievedProps.lookup("System.Photo.ExposureTime") ?
retrievedProps.lookup("System.Photo.ExposureTime") * 1000 + " ms" : "";

var fNumberText = retrievedProps.lookup("System.Photo.FNumber") ?
retrievedProps.lookup("System.Photo.FNumber").toFixed(1) : "";

// Опущено: код для конвертации ImageProperties.latitude и ImageProperties.longitude в
// градусы, минуты, секунды, и направление

return {
"title": ImageProperties.title,
"keywords": ImageProperties.keywords, // строковой массив
"rating": ImageProperties.rating, // число 
"dateTaken": ImageProperties.dateTaken, 
"make": ImageProperties.cameraManufacturer, 
"model": ImageProperties.cameraModel, 
"orientation": orientationText,
// Опущено: свойства широты/долготы
"exposure": exposureText, 
"fNumber": fNumberText
};
}
 

Функция displayImageUI, которой передаются эти свойства, в основном просто копирует данные в различные элементы управления. Полезно будет снова отметить, однако, что отображение фотографии реализовано с помощью нашего хорошего друга URL.createObjectURL:

function displayImageUI(file, propertyText) {
id("outputImage").src = window.URL.createObjectURL(file, { oneTimeOnly: true });
 

Небольшой пример работы с ), как мы уже видели в разделе "Списки воспроизведения". Вы можете сейчас вернуться назад и взглянуть на код, приведенный в этом разделе, и вы вполне сможете понять, что происходит. В SDK нет примеров использования VideoProperties и DocumentProperties, работа с ними выполняется по тем же шаблонам, как показано выше для ImageProperties, поэтому написание необходимого кода не вызовет затруднений.

Кроме того, взглянем еще раз на пример "Конфигурация кнопок для мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-Buttons-ea57d8e2), который мы видели ранее, в разделе "Пользовательский интерфейс управления воспроизведением". Он показывает, как использовать музыкальные свойства для того, чтобы получить изображение альбома.

Что касается сохранения свойств, пример "Простая работа с изображениями" демонстрирует это в Сценарии 1. Так как поля, показаные ранее, на рис. 6.2, можно редактировать, в примере есть кнопка Apply (Применить), которая вызывает функцию applyHandler, показанную ниже, для записи измененных свойств в файл:

function applyHandler() {
ImageProperties.title = id("propertiesTitle").value;

// Ключевые слова хранятся в строковом массиве.
 Разделение строк знаками перевода строки.
ImageProperties.keywords.clear();	
if (id("propertiesKeywords").value !== "") {	
var keywordsArray = id("propertiesKeywords").value.split("\n");	

keywordsArray.forEach(function (keyword) {	
ImageProperties.keywords.append(keyword);
});	
}	

var properties = new Windows.Foundation.Collections.PropertySet();

// При записи оценок, используйте ключ свойства "System.Rating".	
// ImageProperties.rating не обрабатывает значение оценки 0 (нет звезд/оценок).
properties.insert("System.Rating", Helpers.convertStarsToSystemRating(	
id("propertiesRatingControl").winControl.userRating	
));	

// Код опущен: конвертирование дискретных значений широты и
 долготы из пользовательского интерфейса в
// форму, подходящую для свойства, и проверка данных; 
конечный результат этих манипуляций
// сохраняется в списке свойств
properties.insert("System.GPS.LatitudeRef", latitudeRef); properties.insert("System.GPS.LongitudeRef", longitudeRef); 
properties.insert("System.GPS.LatitudeNumerator", latNum); properties.insert("System.GPS.LongitudeNumerator", longNum); 
properties.insert("System.GPS.LatitudeDenominator", latDen);
properties.insert("System.GPS.LongitudeDenominator", longDen);

// Запись массива свойств в файл

ImageProperties.savePropertiesAsync(properties).done(function () {	
// ...	
}, function (error) {	
// Обработка некоторых ошибок и некоторые свойства могут
 не поддерживаться всеми форматами изображений.
});	
}	
 

Несколько примечательных особенностей этого кода перечислены ниже:

  • Он разделяет ключевые слова в элементе управления пользовательского интерфейса и отдельно добавляет каждое к вектору в свойстве keywords.
  • Он создает новую коллекцию свойств типа Windows.Foundation.Collections.PropertySet и использует ее метод insert для добавления свойств в список. Этот набор свойств – именно то, чего ожидает метод savePropertiesAsync.
  • Метод ) особо описывает подобное преобразование.
  • В итоге, вся детализированная информация, которая нужна вам для любого конкретного свойства Windows, может быть найдена на справочной странице этого свойства. Начать углубленное изучение свойств можно со страницы Свойства Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977.aspx).

    Манипуляция изображениями и их кодирование

    Для того, чтобы сделать с изображениями нечто большее, чем загрузка и отображение (где вы можете применить различные CSS-трансформации для реализации эффектов), вам нужно получить доступ к пикселям с помощью декодера (decoder). Это уже происходит внутри механизмов вывода изображения, когда вы присваиваете URI img.src, но для того, чтобы получить непосредственный доступ к пикселям, нужно выполнить ручное декодирование изображения. С другой стороны, сохранение пиксельной информации в виде файла требует использования кодера.

    WinRT предоставляет API и для того, и для другого в пространстве имен ), а именно, в классах BitmapDecoder, BitmapTransform, и BitmapEncoder. Загрузка, выполнение манипуляций с изображением и сохранение его в файл часто включает в себя использование всех трех классов, хотя объект BitmapTransform предназначен для поворота и масштабирования, таким образом, вы не будете пользоваться им, если выполняете другие манипуляции.

    Демонстрацию этого API можно найти в Сценарии 2 примера "Простая работа с изображениями" (http://code.msdn.microsoft.com/windowsapps/Simple-Imaging-Sample-a2dec2b0). Я оставляю вам этот код для того, чтобы вы просмотрели его в редакторе, так как он довольно сложен – до 11 promise-вызовов, объединенных в цепочку для сохранения файла! Так же он выполняет декодирование, манипуляцию и кодирование внутри одной функции, такой, как saveHandler (js/scenario2.js). Вот какую последовательность действий реализует этот код:

  • Открыть файл с помощью StorageFile.openAsync, получив поток.
  • Передать поток статическому методу BitmapDecoder.createAsync, что позволит получить конкретный экземпляр BitmapDecoder для потока.
  • Переать декодер статическому методу BitmapEncoder.createForTranscodingAsync, который предоставляет экземпляр BitmapEncoder. Этот кодировщик создается с помощью InMemoryRandomAccessStream.
  • Установить свойства в свойстве кодировщика bitmapTransform property (объект BitmapTransform) для настройки масштабирования и поворота. Это позволит создать трансформированное изображение в потоке в памяти.
  • Создать набор свойств (Windows.Graphics.Imaging.BitmapPropertySet), который включает в себя System.Photo.Orientation и использует команду кодера bitmapProperties.setPropertiesAsync для того, чтобы это сохранить.
  • Копирование потока из памяти в выходной файловых поток с использованием Windows.Storage.Stream.RandomAccessStream.copyAsync.
  • Закрытие обоих потоков с помощью соответствующих методов (тем самым закрывается файл).
  • В сложных сценариях, подобных этому, полезно рассмотреть отдельные стадии этого процесса, для этой цели в материалах к этой лекции есть упражнение ImageManipulation. Он позволит вам выбрать и загрузить изоражение, конвертировать его в оттенки серого и сохранить конвертированное изображение в новый файл. Результат работы показан на рис. 6.3. Кроме того, это дает вам возможность, как мы можем отправлять декодированное изображение в элемент HTML canvas и сохранять содержимое элемента в виде файла. (рис 6.3) Результаты работы упражнения ImageManipulation из дополнительных материалов к лекции

    Обработчик кнопки Load Image (Загрузить изображение) (loadImage в js/default.js) предоставляет первоначальное отображение. Он позволяет вам выбирать изображение с помощью средства вабра файлов, отображает полноразмерное изображение в элементе img с помощью URL.createObjectURL, вызывает StorageFile.properties.getImagePropertiesAsync для получения свойств title и dateTaken, и применяет StorageFile.getThumbnailAsync для получения эскиза, который отображается в верхней части. Мы уже видели все эти API в действии.

    При щелчке на кнопке Grayscale (Оттенки серого), мы входим в обработчик setGrayscale, где происходят интересные действия. Мы вызываем StorageFile.openReadAsync для того, чтобы получить поток, вызываем BitmapDecoder.createAsync с этим потоком для того, чтобы получить декодер, кэшируем некоторые подробности из декодера в локальном объекте (encoding), вызываем BitmapDecoder.getPixelDataAsync и копируем эти пиксели в элемент canvas (и здесь лишь три асинхронных операции, объединенных в цепочку!):

     var Imaging = Windows.Graphics.Imaging; //Короткое имя	
    var imageFile;            //Полученный из средства выбора файла	
    var decoder;           //Полученный из BitmapDecoder.createAsync	
    var encoding = {};        //Для кэширования некоторых деталей из декодера
    
    function setGrayscale() {
    //Декодируем файл изображения в пиксельные данные для canvas
    
    //Получаем входной поток длф файла (объект StorageFile полученный после открытия)
    imageFile.openReadAsync().then(function (stream) {	
    //Создаем декодер, используя статический метод createAsync и поток файла
    return Imaging.BitmapDecoder.createAsync(stream);	
    }).then(function (decoderArg) {	
    decoder = decoderArg;	
    
    //Настраиваем декодер, если хотим. 
    Параметры по умолчанию, это BitmapPixelFormat.rgba8 и
    //BitmapAlphaMode.ignore. 
    Параметризованная версия getPixelDataAsync может так же
    //управлять трансформацией, а так же ExifOrientationMode, 
    и ColorManagementMode если нужно.
    
    //Кэшируем эти параметры для кодирования	
    encoding.dpiX = decoder.dpiX;	
    encoding.dpiY = decoder.dpiY;	
    encoding.pixelFormat = decoder.bitmapPixelFormat;
    encoding.alphaMode = decoder.bitmapAlphaMode;	
    encoding.width = decoder.pixelWidth;	
    encoding.height = decoder.pixelHeight;	
    
    return decoder.getPixelDataAsync();
    }).done(function (pixelProvider) {
    //detachPixelData получает реальные пиксели (массив нельзя вернуть из 
    //асинхронной операции)
    copyGrayscaleToCanvas(pixelProvider.detachPixelData(), decoder.pixelWidth, decoder.pixelHeight);
    });
    }
    

    Метод декодера ) существует в двух формах. Более простая форма, показанная здесь, выполняет декодирование с использованием настроек по умолчанию. Версия, которую можно полностью контролировать, позволяет вам задать другие параметры, как пояснено в комментариях к вышеприведенному коду. Обычное использование этого метода ведет к выполнению трансформации с использованием объекта Windows.Graphics.Imaging.BitmapTransform (как упомянуто выше), что включает в себя масштабирование (с различными моделями интерполяции), поворот (с приращением в 90 градусов), обрезку и отражение.

    В любом случае, то, что вы получаете из ), единственный восхитительный синхронный метод которого detachPixelData дает вам необходимый массив. (И этот метод можно вызвать лишь один раз, он не будет работать при последующих вызовах, отсюда и имя "detach" (отсоединить)). В итоге мы получаем данные, которые нам нужны для манипуляции пикселями и отображения результатов в элементе canvas, что и демонстрирует функция copyGrayscaleToCanvas. Вы можете, конечно, заменить эту функцию любой подпрограммой для манипуляции пикселями:

    function copyGrayscaleToCanvas(pixels, width, height) {
    //Настраиваем контекст canvas и получаем его пиксельный массив
    var canvas = document.getElementById("canvas1");	
    canvas.width = width;	
    canvas.height = height;	
    var ctx = canvas.getContext("2d");	
    
    //Обходим данные и копируем значения пикселей 
    в canvas после конвертации в оттенки серого
    var imgData = ctx.createImageData(canvas.width, canvas.height);	
    var colorOffset = { red: 0, green: 1, blue: 2, alpha: 3 };	
    var r, g, b, gray;	
    var data = imgData.data; //Очень влияет на производительность!	
    
    for (var i = 0; i < pixels.length; i += 4) {
    r = pixels[i + colorOffset.red];	
    g = pixels[i + colorOffset.green];	
    b = pixels[i + colorOffset.blue];	
    
    //Присваиваем каждое rgb-значение яркости
    gray = Math.floor(.3 * r + .55 * g + .11 * b);
    
    data[i + colorOffset.red] = gray;	
    data[i + colorOffset.green] = gray;	
    data[i + colorOffset.blue] = gray;	
    data[i + colorOffset.alpha] = pixels[i + colorOffset.alpha];
    }	
    
    //Показываем результат в элементе canvas ctx.putImageData(imgData, 0, 0);
    
    //Активируем кнопку сохранения 
    document.getElementById("btnSave").disabled = false;
    }
     

    Это – хороший случай убедиться в том, что JavaScript – это не лучшия язык для работы с большими группами пикселей вроде этой, хотя в данном случае производительность Release build (построения Выпуска), запущенного вне отладчика, весьма неплоха. Подобные подпрограммы лучше реализовывать в виде WinRT-компонентов на языках вроде C# или C++ и делать вызываемыми из JavaScript. У нас будет возможность сделать это в лекции 5 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", где мы так же увидим ограничения элемента canvas, которые требуют несколько иного подхода.

    Сохранение данных из canvas в файл происходит в функции saveGrayscale, где мы используем средство выбора файла для получения StorageFile, открываем поток, берем пиксельные данные из элемента canvas и передаем их BitmapEncoder:

    function saveGrayscale() {	
    var picker = new Windows.Storage.Pickers.FileSavePicker();	
    picker.suggestedStartLocation =	
    Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
    picker.suggestedFileName = imageFile.name + " - grayscale";	
    picker.fileTypeChoices.insert("PNG file", [".png"]);	
    
    var imgData, fileStream = null;
    
    picker.pickSaveFileAsync().then(function (file) {
    if (file) {
    return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
    } else {
    return WinJS.Promise.wrapError("No file selected");
    }
    }).then(function (stream) {
    fileStream = stream;
    var canvas = document.getElementById("canvas1");
    var ctx = canvas.getContext("2d");
    imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    
    return Imaging.BitmapEncoder.createAsync( Imaging.BitmapEncoder.pngEncoderId, stream);
    }).then(function (encoder) {
    //Задаем пиксельные данные, подразумевая, что объект "encoding" 
    имеет необходимые параметры.
    //Конверсия из данных элемента canvas в Uint8Array необходима, 
    так как тип массива из элемента canvas
    //не соответствует тому, что нужно здесь WinRT.
    encoder.setPixelData(encoding.pixelFormat, encoding.alphaMode,
    encoding.width, encoding.height, encoding.dpiX, encoding.dpiY,
    new Uint8Array(imgData.data));
    
    //Начинаем кодирование
    return encoder.flushAsync();
    }).done(function () {
    fileStream.close();
    }, function () {
    //Пустой обработчик ошибки (если пользователь закрыл средство выбора файлов, 
    ничего не происходит)
    });
    }
     

    Обратите внимание на то, как BitmapEncoder принимает идентификатор кодека в первом параметре. Мы используем pngEncoderId, который, как вы можете видеть, определен как статическое свойство класса Windows.Graphics.Imaging.BitmapEncoder; другие возможные значения – это bmpEncoderId, gifEncoderId, jpegEncoderId, jpegXREncoderId, и tiffEncoderId. Все это – форматы, поддерживаемые API. Вы можете установить дополнительные свойства BitmapEncoder прежде чем зададите пиксельные данные, такие, как его BitmapTransform, эти установки будут применены при кодировании.

    Одна особенность, о которой нужно знать, заключается в том, что пиксельный массив, полученный из элемента canvas (тип DOM ) не совместима напрямую с байтовым массивом WinRT, который нужен кодировщику. По этой причине мы используем вызов new Uint8Array в последнем параметре.

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

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

    Во-первых, это метод ), который был кратко упомянут в контексте примера "Простая обработка изображений". С его помощью создается новый кодировщик, который инициализируется на основе существующего объекта ), параметры прозрачности (premultiplied, straight, или ignore, смотрите BitmapAlphaMode ( http://msdn.microsoft.com/library/windows/apps/windows.graphics.imaging.bitmapalphamode.aspx ) размер изображения, DPI изображения, и, конечно, сами пиксельные данные. Помимо этого, вы можете изменить другие свойства посредством метода кодировщика ) (ничего себе имя метода!). Кодировщик позволяет вызывать лишь bitmapProperties.setPropertiesAsync, bitmapProperties. getPropertiesAsync, и flushAsync, и так как это подразумевает неизменность исходных данных, это выполняется быстрее, чем при использовании его более гибкого эквивалента и требует меньше памяти.

    Кодировщик из createForTranscodingAsync не приспособлен для изменения формата файла изображения (например, JPEG на PNG); для этого вам нужно использовать createAsync, где вы можете задать конкретный тип кодирования. Как мы уже видели, первый аргумент createAsync это идентификатор кодека, куда обычно передают одно из статических свойств из Windows.Graphics.Imaging.BitmapEncoder. Я еще не упоминал о том, что вы можете так же задавать пользовательские кодеки в этом первом параметре, и что createAsync может так же поддерживать необязательный третий аргумент, в котором можно передать параметры для конкретного кодека. Однако здесь имеются некоторые сложности и ограничения.

    Позвольте мне сначала остановиться на параметрах. Существующая документация по значениям кодеков BitmapEncoder (наподобие pngEncoderId) не содержит многих подробностей о доступных параметрах. Для того, чтобы их узнать, вам нужно обратиться к документации по Windows Imaging Component (WIC), в частности, к материалу Native WIC Codecs (http://msdn.microsoft.com/library/windows/desktop/gg430027.aspx), который посвящен тому, что WinRT предоставляет приложениям для Магазина Windows. Если вы пройдете на страницу конкретного кодека, вы увидите раздел "Encoder Options" (Свойства кодера), который содержит описания того, что можно использовать. Например, кодек JPEG (http://msdn.microsoft.com/library/windows/desktop/gg430026.aspx) поддерживает свойства наподобие ) поддерживает свойства наподобие FilterOption для различных оптимизаций сжатия файлов.

    Для того, чтобы задать эти свойства, вам нужно создать новый BitmapPropertySet и вставить в этот набор запись для каждого необходимого параметра. Если, например, у вас есть переменная, содержащая данные о качестве изображения, которое вы хотите использовать при кодировании JPEG, вы можете создать кодер, таким образом:

    var options = new Windows.Graphics.Imaging.BitmapPropertySet();	
    options.insert("ImageQuality", quality);	
    var encoderPromise = Imaging.BitmapEncoder.createAsync(Imaging.BitmapEncoder.jpegEncoderId,
    stream, options);	
     

    Вы используете тот же самый BitmapPropertySet для любых свойств, которые вы можете передать при вызове метода кодера bitmapProperties.setPropertiesAsync. Здесь мы используем тот же механизм для свойств кодировщика.

    Что касается пользовательских кодеков, это просто означает, что первый аргумент ). Загвоздка здесь в том, что включение пользовательских кодеков для изображений в пакет приложения не поддерживается. Если кодек уже присутствует в системе (то есть, установлен традиционным образом), это будет работать. Однако, политика Магазина Windows не позволяет одним приложениям зависеть от других, так что маловероятно, что вы сможете хотя бы отправить подобное приложение, если только оно не предустановлено на каком-то конкретном OEM-устройстве и нужная DLL не является частью образа системы (Приложения, написанные на C++ могут большее в да нной области, но это выходит за границы данного курса).Короче говоря, в случае с приложениями, написанными на JavaScript и HTML, вы, в работе с различными форматами изображений, по-настоящему ограничены применением кодеков, которые изначально поддерживаются системой.

    Заметьте, что эти ограничения не применяются для пользовательских аудио- и видеокодеков. Пример "Расширения мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-extensions-sample-7b466096) показывает, как работать с пользовательским видеокодеком, что мы увидим в следующем разделе.

    Манипуляция аудио и видео

    Как и при работе с изображения, если все, что нам нужно – это загрузить содержимое StorageFile в аудио- или видеоэлемент, мы можем просто передать этот StorageFile в URL.createObjectUrl и пприсвоить результат атрибуту src. Похожим образом, если нам нужны исходные данные, мы можем просто использовать методы StorageFile.openAsync или openReadAsync для получения файлового потока.

    Честно говоря, открытие файла, это, вероятнее всего, самое сложное, что вам придется делать в JavaScript с исходными аудио- или видеоданными, если вообще придется этим пользоваться. В то время, как процесс обработка изображений находится где-то на грани приемлемости в окружении JavaScript, обрабатывать аудио, и особенно видео, лучше всего в высокопроизводительных DLL C++. На самом деле, легко доступны множесто платформенно нейтральных библиотек C++ сторонних разработчиков для подобных целей, которые вы сможете легко включить в состав подобных DLL. В подобном случае вы можете просто позволить DLL открыть нужный файл.

    Тем не менее, WinRT предоставляет средства для перекодирования (конвертации) различных мультимедиа-форматов и предоставляет модель расширения для пользовательских кодеков, эффектов и обработчиков схем. На самом деле, мы уже видели, как применять пользовательские видео эффекты в примере "Расширения мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-extensions-sample-7b466096), и те же DLL можно использовать при кодировании, где все, что выполняет код JavaScript – это связывание вместе нужных компонентов (что он делает очень хорошо). Посмотрим как это делается, сначала на примере перекодирования видео, а затем – на примере пользовательского кодека.

    Перекодирование

    Перекодирование аудио и видео реализуется посредством класса ), который поддерживает выходные форматы mp3 и wma для аудио, и mp4, wmv и m4a для видео. Процесс перекодирования позволяет вам применять эффекты, осуществлять обрезку композиций.

    Перекодирование происходит либо из одного объекта ). Для настройки операции перекодирования, вы вызываете методы ) или ). Он представляет собой операцию, которая готова к работе, но обработка не начинается, пока вы не вызовете его метод transcodeAsync. В JavaScript, подобный результат является promise-объектом, который позволяет вам предоставлять обработчики завершения и выполнения для отдельной операции, а так же позволяет комбинировать операции с помощью WinJS.Promise.join. Это позволяет сначала настроить операции, а потом запустить их выполнение, что полезно для пакетной обработки и выполнения автоматической отправки на сервисы наподобие YouTube, пока вы спите! (В подобное время я вытаскиваю упаковку льда из морозилки и кладу ее под мой ноутбук из-за его плохой системы охлаждения)

    Пример "Перекодирование мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-Transcode-Sample-f7ba5269) показывает нам пару сценариев перекодирования. В Сценарии 1 (js/presets.js) мы можем выбрать видеофайл, задать целевой формат, выбрать профиль перекодирования и предоставить компьютеру возможность сделать свою работу (информация о продвижении операции предоставляется), как показано на рис. 6.4. (рис 6.4) Пример перекодирования мультимедиа обрабатывает клип с моим двухлетним сыном, который исследует измерительную рулетку

    Код, который выполняется, когда вы нажимаете на кнопку Transcode (Перекодировать) приведен ниже (кое-что опущено; в примере использованы вложенные promise-вызовы, что не рекомендуется, если вам нужна обработка ошибок, этот код поглощает любые исключения, которые происходят до вызова transcodeAsync):

    function onTranscode() {	
    // Создадим объект для перекодирования.	
    var transcoder = null;	
    transcoder = new Windows.Media.Transcoding.MediaTranscoder();
    
    // Получим профиль перекодирования.
    getPresetProfile(id("profileSelect"));
    
    // Создадим выходной файл и начнем перекодировку.	
    var videoLib = Windows.Storage.KnownFolders.videosLibrary;	
    var createFileOp = videoLib.createFileAsync(g_outputFileName,	
    Windows.Storage.CreationCollisionOption.generateUniqueName);
    
    createFileOp.done(function (ofile) {	
    g_outputFile = ofile;	
    g_transcodeOp = null;	
    var prepareOp = transcoder.prepareFileTranscodeAsync(g_inputFile, g_outputFile,
    g_profile);	
    
    prepareOp.done(function (result) {
    if (result.canTranscode) {
    g_transcodeOp = result.transcodeAsync();
    g_transcodeOp.done(transcodeComplete, transcoderErrorHandler, transcodeProgress);
    } else {
    transcodeFailure(result.failureReason);
    }
    }); // prepareOp.done 
    id("cancel").disabled = false;
    }); // createFileOp.done
    }
     

    Метод getPresentProfile получает подходящий объект профиля, соответствующий параметрам, выбранным в приложении. Для параметров, выбранных на рис. 6.4 (WMV и WVGA) мы используем следующие части этой функции:

    function getPresetProfile(profileSelect) {	
    g_profile = null;	
    var mediaProperties = Windows.Media.MediaProperties;
    var videoEncodingProfile;	
    
    switch (profileSelect.selectedIndex) {
    // Остальные варианты опущены 
    case 2:
    videoEncodingProfile = mediaProperties.VideoEncodingQuality.wvga;
    break;
    }
    if (g_useMp4) {
    g_profile = mediaProperties.MediaEncodingProfile.createMp4(videoEncodingProfile);
    } else {
    g_profile = mediaProperties.MediaEncodingProfile.createWmv(videoEncodingProfile);
    }
    }
     

    В Сценарии 2 всегда используется WVGA-коодирование, но здесь вы можете установить необходимые значения для размерностей видео, частоты кадров, битрейта аудио и видео, настроить аудиоканалы и аудиосэмплинг. Данные параметры используются в getCustomProfile (js/custom.js), просто конфигурируя параметры профиля после его создания:

    function getCustomProfile() {	
    if (g_useMp4) {	
    g_profile = Windows.Media.MediaProperties.MediaEncodingProfile.createMp4(
    Windows.Media.MediaProperties.VideoEncodingQuality.wvga);	
    } else {	
    g_profile = Windows.Media.MediaProperties.MediaEncodingProfile.createWmv(
    Windows.Media.MediaProperties.VideoEncodingQuality.wvga);	
    }	
    
    // Берем значения настроек из элементов пользовательского интерфейса 
    g_profile.audio.bitsPerSample = id("AudioBPS").value; 
    g_profile.audio.channelCount = id("AudioCC").value; 
    g_profile.audio.bitrate = id("AudioBR").value; 
    g_profile.audio.sampleRate = id("AudioSR").value; 
    g_profile.video.width = id("VideoW").value; 
    g_profile.video.height = id("VideoH").value; 
    g_profile.video.bitrate = id("VideoBR").value; 
    g_profile.video.frameRate.numerator = id("VideoFR").value; 
    g_profile.video.frameRate.denominator = 1;
    }
     

    И, в завершении, Сценарий 3 похож на Сценарий 1, но позволяет вам задать время начала и конца записи для ее обрезки, которые затем сохраняются в свойствах перекодировщика trimStartTime и trimStopTime (js/trim.js):

    transcoder = new Windows.Media.Transcoding.MediaTranscoder();
    transcoder.trimStartTime = g_start;
    transcoder.trimStopTime = g_stop;
     

    Хотя это и не показано в примре, вы можете применять эффекты при операции перекодирования, используя методы перекодировника addAudioEffect и addVideoEffect.

    Пользовательские кодеры/декодеры и обработчики схем

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

    Как было сказано ранее, о пользовательских форматах изображений, эти вопросы ведут в весьма обширную область вокруг Windows Media Foundation (WMF) SDK (http://msdn.microsoft.com/library/ms694197.aspx). И WinRT лишь предоставляет оболочку для этого, поэтому изучение WMF существенно в этих вопросах, и это занятие не для слабонервных!

    Аудио- и видеорасширения объявляются в манифесте приложения, вам понадобится непосредственно править XML. Как можно увидеть в примере "Расширения мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-extensions-sample-7b466096), для каждой DLL в решении объявление выглядит так:

     <Extension Category="windows.activatableClass.inProcessServer">
    <InProcessServer>	
    <Path>MPEG1Decoder.dll</Path>	
    <ActivatableClass ActivatableClassId="MPEG1Decoder.MPEG1Decoder"
    ThreadingModel="both" />	
    </InProcessServer>	
    </Extension>	
    

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

    В зависимости от использования расширения, вам может понадобиться зарегистрировать его в WinRT посредством методов Windows.Media.MediaExtensionManager: registerAudio[Decoder | Encoder], registerByteStreamHandler (потоки мультимедиа), registerSchemeHandler (источники мультимедиа/файлы-контейнеры), и registerVideo[Decoder | Encoder] . В сценарии 1 примера о расширениях мультимедиа (js/LocalDecoder.js), мы можем видеть, как задать пользовательский декодер для воспроизведения видео.

     воспроизведения видео.
    var page = WinJS.UI.Pages.define("/html/LocalDecoder.html", {	
    extensions: null,	
    MFVideoFormat_MPG1: { value: "{3147504d-0000-0010-8000-00aa00389b71}" },
    NULL_GUID: { value: "{00000000-0000-0000-0000-000000000000}" },	
    
    ready: function (element, options) {
    if (!this.extensions) {
    // Добавьте здесь любой код инициализации
    this.extensions = new Windows.Media.MediaExtensionManager();
    // Зарегистрируем пользовательский ByteStreamHandler и пользовательский декодер. 
    this.extensions.registerByteStreamHandler("MPEG1Source.MPEG1ByteStreamHandler",
    ".mpg", null);
    this.extensions.registerVideoDecoder("MPEG1Decoder.MPEG1Decoder", this.MFVideoFormat_MPG1, this.NULL_GUID);
    }
    
    // ...
    

    Здесь CLSID MPEG1Source.MPEG1ByteStreamHandler реализован в одной DLL (смотрите проект C++ MPEG1Source в решении), а CLSID MPEG1Decoder.MPEG1.Decoder реализован в другой (C++ проект MPEG1Decoder).

    Сценарий 2, в свою очередь, показывает использование пользовательского обработчика схемы, где обработчик (в проекте C++ GeometricSource) генерирует видеокадры в процессе выполнения программы. Притягательная возможность, но находящаяся за пределами тем данного курса.

    Эффекты, как мы можем видеть, довольно просто использовать, если у вас есть реализация эффекта, просто передайте ее CLSID методу наподобие msInsertVideoEffect и msInsertAudioEffect видео или аудиоэлемента. Так же вы можете применить эффект в процессе перекодирования, в методах addAudioEffect и addVideoEffect класса MediaTranscoder. То же самое справедливо и для процесса захвата мультимедийных данных, который мы сейчас рассмотрим.

    Захват мультимедийных данных

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

    И бывают ситуации, когда мы хотим включить камеру или микрофон и что-то записать: сделать фотографию, аудиозапись или видеоклип. Конечно, приложения не могут знать заранее, какие именно камеры и микрофоны могут быть установлены в системе. Ключевой шаг в захвате мультимедийных данных заключается в определении устройства для использования – а это то, что красиво предоставляет нам API Windows.Media.Capture, вместе со средствами для выполнения захвата в файл, поток или в какой-нибудь пользовательский объект, в зависимости от того, как приложение хочет обрабатывать захваченные данные или манипулировать ими.

    В лекции 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", в приложении "Here My Am!", мы видели, как просто WinRT позволяет делать фотографии. Нам лишь нужно объявить возможность Веб-камера (Webcam) в манифесте и добавить несколько строк кода:

    function capturePhoto() {
    var that = this;
    
    var captureUI = new Windows.Media.Capture.CameraCaptureUI();
    
    //Показывает, что мы хотим захватить PNG-файл, размером не больше, чем целевй элемент --	
    //пользовательский интерфейс автоматически выведет рамку, обрезающую изображение под этот размер	
    captureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
    captureUI.photoSettings.croppedSizeInPixels =	
    { width: this.clientWidth, height: this.clientHeight };	
    
    captureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo)
    .done(function (capturedFile) {
    //Убедитесь, что проверили, подходит ли вам возвращенный элемент: он может содержать null, если
    //пользователь отменил действие. 
    if (capturedFile) {
    lastCapture = capturedFile; //Сохраним для целей общего доступа
    that.src = URL.createObjectURL(capturedFile, {oneTimeOnly: true});
    }
    }, function (error) {
    console.log("Unable to invoke capture UI.");
    });
    }
     

    Пользовательский интерфейс, который Windows предоставляет нам посредством данного API, предоставляет настройки для обрезки и повторной съемки изображения, и для установки параметров камеры. Другой пример получения фотографии можно найти в Сценарии 1 примера "Пользовательский интерфейс камеры" (http://code.msdn.microsoft.com/windowsapps/CameraCaptureUI-Sample-845a53ac), вместе с примером захвата видео в Сценарии 2. В последнем случае (js/capturevideo.js) мы настраиваем объект интерфейса захвата данных на видеоформат и указываем видеорежим при вызове captureFileAsync:

    function captureVideo() {
    var dialog = new Windows.Media.Capture.CameraCaptureUI();
    dialog.videoSettings.format = Windows.Media.Capture.CameraCaptureUIVideoFormat.mp4;
    
    dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.video)
    .done(function (file) {
    if (file) {
    var videoBlobUrl = URL.createObjectURL(file, {oneTimeOnly: true});
    } else {
    //...
    }
    }, function (err) {
    //...
    });
    }
     

    Следует отметить, что возможность Веб-камера (Webcam) в манифесте применима лишь для захвата фотографий или записи видео без звука. Если вы хотите захватывать аудиосигнал, убедитесь в том, что объявили возможность Микрофон (Microphone) на закладке Возможности (Capabilities) редактора манифеста.

    Если вы взглянете на объект ), который включает в себя сведения по управлению вызовами на устройстве связи Bluetooth.

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

    Все эти возможности доступны посредством класса ):

    Свойства Описание (классы в пространстве имен Windows.Media.Capture если не указано иное)
    audioController Свойство AudioDeviceController, которое управляет громкостью и предоставляет возможность управлять другими свойствами, воздействующими на аудиопоток.
    mediaCaptureSettings Свойство MediaCaptureSettings, содержажее ID устройств и параметры режима, и позволяет устанавливать источник (audio, videoPreview, photo).
    videoController Свойство VideoDeviceController, которое управляет свойствами изображения (яркостью, оттенком, панорамированием/наклоном, масштабом и так далее), предоставляя настройки для учета задней подсветки объекта и частоты переменного тока, и предоставляет возможность управления другими свойствами, воздействующими на видеопоток.
    События Описание
    failed Вызывается, когда в поцессе захвата происходит ошибка.
    recordLimitationExceeded Вызывается, когда пользователь пытается записать звук или видео, длительность которых превышает разрешенную.
    Методы Описание
    initializeAsync Инициализирует объект MediaCapture (с параметрами по умолчанию или с использованием объекта MediaCaptureInitializationSettings, который содержит то же самое, что содержится в MediaCaptureSettings).
    addEffectAsync Применяет эффект.
    clearEffectsAsync Удаляет все текущие эффекты.
    capturePhotoToStorageFileAsync capturePhotoToStreamAsync Захватывает изображение в файл или в поток с произвольным доступом. Оба варианта принимают экземпляр ), как описано ранее, в разделе "Общие свойства файлов"
    getEncoderProperty setEncoderProperty Управляет специфическими свойствами кодера.
    startRecordToStorageFileAsync startRecordToStreamAsync stopRecordAsync Начинают и останавливают запись в файл или в поток, используя MediaEncodingProfile, который определяет формат аудио или видео, вместе с битрейтом, качеством, размерами видео и так далее.
    getRecordRotation setRecordRotation Применимы для видео, работают со значением VideoRotation (с 90-градусным приращением) в применении к записи. Не воздействуют на аудио.
    startRecordToCustomSinkAsync Начинает запись в пользовательский объект, который описан либо в виде реализации Windows.Media.IMediaExtension либо как ID, с использованием набора параметров.
    startPreviewAsync startPreviewToCustomSinkAsync stopPreviewAsync getPreviewRotation setPreviewRotation Выполняют те же функции, что и при записи, но применяются для целей предварительного просмотра. В данном случае, если вы вызовете URL.createObjectURL и передадите объект MediaCapture в качестве перевого параметра, полученный результат может быть присвоен атрибуту src элемента video и данные предварительного просмотра будут отображены в этом элементе, когда вы вызовете его метод video.play.
    getPreviewMirroring setPreviewMirroring Управляют зеркальным отражением при предварительном просмотре, что подразумевает горизонтальное отражение. Это применимо для камер с различной установкой, которые могут быть направлены в сторону от пользователя (камеры, устанавливаемые на задней панели, как на планшетных компьютерах), или в его сторону (камеры, которые монтируют на мониторах или встраивают в экраны ноутбуков). Смотрите следующий раздел: "Выбор устройства захвата мультимедиа".

    Для того, чтобы увидеть очень простую демонстрацию возможностей предварительного просмотра видео в элементе video, мы можем обратиться к примеру "Параметры пользовательского интерфейса камеры" (http://code.msdn.microsoft.com/windowsapps/CameraOptionsUI-Sample-44c06873), к его файлу js/showoptionsui.js. Когда вы нажимаете на кнопку Start Preview (Начать предварительный просмотр), создается и инициализируется объект MediaCapture:

    function initializeMediaCapture() {	
    mediaCaptureMgr = new Windows.Media.Capture.MediaCapture();	
    mediaCaptureMgr.initializeAsync().done(initializeComplete, initializeError);
     }	
     

    Где обработчик initializeComplete вызывается в startPreivew:

    function startPreview() {	
    document.getElementById("previewTag").src = URL.createObjectURL(mediaCaptureMgr);
    document.getElementById("previewTag").play();	
    startPreviewButton.disabled = true;	
    showSettingsButton.style.visibility = "visible";	
    previewStarted = true;
    }
     

    В этом коде так же показан вызов Windows.Media.Capture.CameraOptionsUI , который происходит, когда вы нажимаете на кнопку Show Settings (Показать параметры), результат показан на рис. 6.5. Это всплывающий элемент, предоставленный системой с параметрами, которые соответствуют захватываемому потоку мультимедиа:

     function showSettings() {	
    if (mediaCaptureMgr) {	
    Windows.Media.Capture.CameraOptionsUI.show(mediaCaptureMgr);
    }	
    }	
    

    Кстати, если у вас возникают проблемы с запуском примера, подобного этому, в имитаторе Visual Studio, в частности, если вы сталкиваетесь с исключением, когда пытаетесь включить камеру – попытайтесь, вместо этого, запустить его на локальном компьютере или на удаленной машине. (рис 6.5) Пользовательский интерфейс настроек параметров камеры из примера (пустая нижняя часть обрезана)

    Более сложный сценарий, включающий в себя использование класса ), в частности, это предварительный просмотр и захват видео, динамическое изменение свойств (Сценарий 1), выбор конкретного мультимедиа-устройства (Сценарий 2) и запись звука, без видео (Сценарий 3).Начнем со Сценария 3 (js/AudioCapture.js, он самый простой), здесь присутствует базовый код для создания и инициализации объекта MediaCapture для аудиопотока (смотрите свойство streamingCaptureMode в параметрах инициализации), где поток направляется в файл в библиотеке музыки посредством startRecordToStorageFileAsync (некоторая часть кода опущена для краткости):

    var mediaCaptureMgr = null;	
    var captureInitSettings = null;
    var encodingProfile = null;	
    var storageFile = null;	
    
    // Это вызывается при загрузке страницы	
    function initCaptureSettings() {	
    captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
    captureInitSettings.audioDeviceId = "";	
    captureInitSettings.videoDeviceId = "";	
    captureInitSettings.streamingCaptureMode =	
    Windows.Media.Capture.StreamingCaptureMode.audio;
     }	
    
    function startDevice() {
    mediaCaptureMgr = new Windows.Media.Capture.MediaCapture();
    
    mediaCaptureMgr.initializeAsync(captureInitSettings).done(function (result) {
    // ...	
    });	
     }	
    
    function startRecord() {
    // ...
    // Начинаем запись. Windows.Storage.KnownFolders.videosLibrary.createFileAsync("cameraCapture.m4a",
    Windows.Storage.CreationCollisionOption.generateUniqueName)
    .done(function (newFile) {
    storageFile = newFile;
    encodingProfile = Windows.Media.MediaProperties
    .MediaEncodingProfile.createM4a(Windows.Media.MediaProperties
    .AudioEncodingQuality.auto);
    mediaCaptureMgr.startRecordToStorageFileAsync(encodingProfile, storageFile).done(function (result) {
    // ...
    });
    });
    }
    
    function stopRecord() {
    mediaCaptureMgr.stopRecordAsync().done(function (result) {
    displayStatus("Record Stopped. File " + storageFile.path + " ");
    
    // Воспроизведение записанного аудио	
    var audio = id("capturePlayback" + scenarioId);	
    audio.src = URL.createObjectURL(storageFile, { oneTimeOnly: true });
    audio.play();	
    });	
     }	
    
     

    Сценарий 1, в целом, использует тот же код, но захватывает видеопоток и фотографии, результат его работы показан на рис. 6.6. Эти особенности включены посредством следующих свойств в установках инициализации (смотрите js/BasicCapture.js, внутри initCaptureSettings):

    captureInitSettings.photoCaptureSource =	
    Windows.Media.Capture.PhotoCaptureSource.videoPreview;	
    captureInitSettings.streamingCaptureMode =	
    Windows.Media.Capture.StreamingCaptureMode.audioAndVideo;
     

    (рис 6.6) Предварительный просмотр и запись видео с устройства по умолчанию в Сценарии 1 примера о захвате мультимедиа. (Изображение обрезано, так как мне понадобилось запустить этот пример с использованием опции Локальный компьютер в Visual Studio, и я не думаю, что вам надо видеть скриншот размером 1920х1200 с большим количеством пустого пространства!)

    Обратите внимание на элементы управления Contrast (Контраст) и Brightness (Яркость) на рис. 4.9. Их изменение меняет видео во время предварительного просмотра, вместе с записываемым видео. В примере это реализовано с помощью свойств contrast и brightness объекта MediaCapture.videoDeviceController, демонстрируя возможность динамической настройки этих (и других) свойств контроллера. Обратитесь к функции getCameraSettings в js/BasicCapture.js, которая подключает события change ползунка в обычной анонимной функции для обновления необходимого свойства.

    Выбор устройства захвата мультимедиа

    Взглянем теперь на Сценарий 2 (js/AdvancedCapture.js), он более или менее похож на Сценарий 1, но он позволяет вам выбирать конкретное устройство захвата. До настоящего момента мы пользовались лишь устройством по умолчанию, но вы не ограничены этим, конечно. Вы можете использовать API ) для получения списка устройств в классе интерфейсов устройств. В примере использован заранее определенный класс videoCapture:

    function enumerateCameras() {
    var cameraSelect = id("cameraSelect");
    deviceList = null;
    deviceList = new Array();
    while (cameraSelect.length > 0) {
    cameraSelect.remove(0);
    }
    //Перечисляем вебкамеры и добавляем их в список
    var deviceInfo = Windows.Devices.Enumeration.DeviceInformation;
    deviceInfo.findAllAsync(Windows.Devices.Enumeration.DeviceClass.videoCapture)
    .done(function (devices) {
    // Добавляем устройства в deviceList 
    if (devices.length > 0) {
    for (var i = 0; i < devices.length; i++) {
    deviceList.push(devices[i]);
    cameraSelect.add(new Option(deviceList[i].name), i);
    }
    //Выбираем первую вебкамеру
    cameraSelect.selectedIndex = 0; 
    initCaptureSettings();
    } else {
    // блокируем кнопки.
    }
    }, errorHandler);
    }
     

    Выбранные ID устройств затем копируются внутри initCaptureSettings в свойство MediaCaptureInitializationSetting.videoDeviceId:

     var selectedIndex = id("cameraSelect").selectedIndex; 
    var deviceInfo = deviceList[selectedIndex]; 
    captureInitSettings.videoDeviceId = deviceInfo.id;
    

    Кстати, вы всегда можете получить ID устройства по умолчанию, воспользовавшись методами объекта ) и прослушивая его события на предмет изменения устройств по умолчанию. Важно отметить, что DeviceInformation (в переменной deviceInfo выше) включает в себя свойство, которое называется enclosureLocation. Оно сообщает о том, направлена ли камера на пользователя, или от него, что вы можете использовать для поворота видео или фото так, чтобы их отображение соответствовало восприятию пользователя:

    var cameraLocation = null;
    
    if (deviceInfo.enclosureLocation) {
    cameraLocation = deviceInfo.enclosureLocation.panel;
    }
    
    if (cameraLocation === Windows.Devices.Enumeration.Panel.back) {
    rotateVideoOnOrientationChange = true;
    reverseVideoRotation = false;
    } else if (cameraLocation === Windows.Devices.Enumeration.Panel.front) {
    rotateVideoOnOrientationChange = true;
    reverseVideoRotation = true;
    } else {
    rotateVideoOnOrientationChange = false;
    }
     

    Сценарий 2 демонстрирует, так же, использование MediaCapture.addEffectAsync с эффектом перевода в оттенки серого, что показано на рис. 6.7., который реализован в DLL (проект GrayscaleTransform в решении). Это работает в точности так, как работало уже рассмотренное нами перекодирование, и вы можете обратиться к функциям addRemoveEffect и addEffectToImageStream в файле примера js/AdvancedCapture.js для того, чтобы увидеть подробности. Вы можете здесь заметить, что эти функции выполняют некоторое количество проверок с использованием значения MediaCaptureSettings.videoDeviceCharacteristic для того, чтобы убедиться, что эффект добавлен в верное место. (рис 6.7) Сценарий 2 примера о захвате мультимедиа, где можно выбирать конкретное устройство захвата и применять эффект (Окно обрезано из скриншота большего размера). Вы достаточно внимательно посмотрели на этот рисунок, чтобы заметить, что я поменял гитары?

    Потоковое мультимедиа и целевые объекты воспроизведения

    Грубым преуменьшением будет говорить, что потоковое мультимедиа популярно в наши дни. Как я упоминал во введении к лекции, один только Netflix занимает немалую долю полосы пропускания современного Интернета (и в моем доме – тоже). YouTube тоже не стоит в стороне – и ваше приложение так же может внести свой вклад в это дело!

    Передача мультимедийных данных с сервера приложению это простой и наболее распространенный случай. Это происходит автоматически, когда вы устанавливаете атрибут ) (он имеет статус бета-версии, когда я это пишу), который призван помочь вам создавать мультимедиа-приложения с большим набором богатых возможностей, включая живое воспроизведение и защиту содержимого PlayReady. Я не описываю в данном курсе этот SDK, я лишь хочу, чтобы вы о нем знали.

    На последних страницах данной лекции мы рассмотрим соображения по управлению цифровыми правами (DRM), и поговорим о потоковой передаче данных, но не из сети, а в сеть. Например, о захвате аудио и видеоинформации коммуникационным приложением, и о передаче приложением потокового мультимедиа на целевые устройства воспроизведения (PlayTo).

    Потоковая передача данных с сервера и DRM

    Потоковая передача мультимедиа с сервера, это то, что вы уже делали, применяя элементы audio и video с URI, указывающими на удаленные ресурсы. Все это выполняется автоматически. На самом деле, все, что делает отличный мультимедиа-клиент – это общается с веб-сервисом, получает метаданные и списки элементов, помогает пользователю перемещаться по этой информации и, в конце концов, получает URI, который можно передать в атрибут src элемента audio или video. Затем, как только приложение получает событие canplay, вы можете вызвать метод play элемента для того, чтобы начать воспроизведение.

    Конечно, мультимедийные записи часто защищены DRM, в противном случае содержимое платных сервисов не приносило бы денег владельцам прав! Таким образом, здесь нужен механизм для получения и проверки прав где-то между установкой атрибута src элемента и получением события canplay. К счастью, есть простые средства для того, чтобы это сделать:

    Перед установкой атрибута ) и настраивается его свойство properties.

    Прослушивается событие этого объекта serviceRequested, обработчик которого производит проверку прав и устанавливает флаг завершения, если все верно. (Два других события, о которых я просто хочу упомянуть, это componentloadfailed и rebootneeded.)

    Присвоение средства управления защитой к элементу audio/video с использованием метода расширения ).

    Установка атрибута src. Это вызовет событие serviceRequested для начала обработки DRM, которая предотвращает вызов canplay до тех пор, пока проверка прав не завершится успешно.

    Вы можете обратиться к материалам "Использование подключаемого DRM" (http://msdn.microsoft.com/library/windows/apps/hh452779.aspx), и "Обработка ошибок DRM" (http://msdn.microsoft.com/library/windows/apps/hh452767.aspx) для того, чтобы узнать подробности, здеь приведен минимальный гипотетический пример кода, который все это реализует:

    var video1 = document.getElementById("video1");
    
    video1.addEventListener('error', function () {
    var error = video1.error.msExtendedCode;	
    //...	
    }, false);	
    
    video1.addEventListener('canplay', function () {
    video1.play();
    }, false);
    
    var cpm = new Windows.Media.Protection.MediaProtectionManager();	
    cpm.addEventListener('servicerequested', enableContent, false); //Удалите это позже
    video1.msSetContentProtectionManager(cpm);	
    video1.src = "http://some.content.server.url/protected.wmv";
    
    
    function enableContent(e) {
    if (typeof (e.request) != 'undefined') {
    var req = e.request;	
    var system = req.protectionSystem;	
    var type = req.type;	
    
    //Предпринять необходимые действия на основе системы и типа
    }
    if (typeof (e.completion) != 'undefined') {
    //Запрошенное действие завершено	
    var comp = e.completion;	
    comp.complete(true);	
    }	
    }	
     

    Как конкретно вы проверяете права, зависит от сервиса, из которого вы получаете данные – и это не то, что вам захочется опубликовать!

    Более полная демонстрация работы с DRM находится в примере "Простое использование PlayReady" (http://code.msdn.microsoft.com/windowsapps/Simple-PlayReady-sample-5c1aefaf), которому нужно, чтобы вы скачали и установили Microsoft PlayReady Client SDK (http://visualstudiogallery.msdn.microsoft.com/e02ccac7-f3eb-4b53-b11a-c657d5631483). PlayReady, если вы еще с ним не знакомы, это сервис лицензирования, который предоставляет Microsoft, таким образом, вам не нужно создавать что-то подобное с нуля. PlayReady Client SDK предоставляет дополнительные инструменты и подержку фреймворка для приложений, которые нуждаются в реализации онлайновых и оффлайновых сценариев работы с мультимедиа, таких, как прогрессивная загрузка, загрузка для владения, выдача напрокат и подписка. В дополнение к этому, с использованием этого SDK вам не нужно отправлять ваше приложение для тестирование на соответствие требованиям DRM. В любом случае, вот как пример использования PlayReady настр аивает свое средство защиты содержимого, просто для того, чтобы показать, как API WinRT используются с конкретными идентификаторами DRM-сервисов:

    mediaProtectionManager = new Windows.Media.Protection.MediaProtectionManager();
    mediaProtectionManager.properties["Windows.Media.Protection.MediaProtectionSystemId"] =
    '{F4637010-03C3-42CD-B932-B48ADF3A6A54}'
    
    var cpsystems = new Windows.Foundation.Collections.PropertySet();	
    cpsystems["{F4637010-03C3-42CD-B932-B48ADF3A6A54}"] =	
    "Microsoft.Media.PlayReadyClient.PlayReadyWinRTTrustedInput";	
    mediaProtectionManager.properties[	
    "Windows.Media.Protection.MediaProtectionSystemIdMapping"] = cpsystems;
     

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

    Следующий случай касается ситуации, когда приложение является источником потоковых мультимедиа-данных, вместо того, чтобы быть их потребителем, что означает, что клиентские приложения в других местах выступают в таком качестве. На самом деле, в этом сценарии – аудио- или видеокоммуникации и реализации конференц-связи – обычно приложение играет обе роли, выполняя потоковую передачу данных другим клиентским приложениям и получая данные от них. Так работают Windows Live Messenger, Skype и другие подобные программы, вместе с приложениями наподобие игр, которые включают в себя возможности чата.

    Вот как такие приложения обычно работают:

  • Настройка необходимых каналов связи через сеть, это могут быть одноранговые системы, или системы с привлечением некоей централизованной службы.
  • Захват аудио и видео в поток с использованием API WinRT, которые мы уже видели (в особенности MediaCapture.startRecordToStreamAsync), или захват в пользовательский объект.
  • Выполнение любой дополнительной обработки данных в потоке. Обратите внимание, однако, что здесь применяются эффекты, подключенные к механизму захвата (MediaCapture.addEffectAsync), вместо тех, которые обрабатывают уже захваченные данные.
  • Кодирование потока для передачи любым необходимым способом.
  • Передача потока через сетевой канал связи.
  • Прием передач от других подключенных приложений.
  • Декодирование переданных приложению потоков и конверсия в большой двоичный объект (blob) с использованием MSApp.createBlobFromRandomAccessStream.
  • Использование URL.createObjectURL для подключения элементов audio и video к потоку.
  • Для того, чтобы увидеть это в действии, обратитесь к примеру "Коммуникация в реальном времени" (http://code.msdn.microsoft.com/Simple-Communication-Sample-eac73290), который реализует видеочат в Сценарии 2 и показывает работу с различными режимами задержки с Сценарии 1. Последние два шага в вышеприведенном списке так же показаны в примере "Приемник целевого объекта воспроизведения" (http://code.msdn.microsoft.com/windowsapps/PlayToReceiver-sample-607f00ed), который настроен на прием мультимедиа-потоков из других источников.

    Целевые объекты воспроизведения

    Последний вариант работы с потоковыми данными построен вокруг возможности передачи данных на целевые объекты воспроизведения (PlayTo), которая была представлена в Windows 7. Проще говоря, эта возможность представляет собой средство, с помощью которого приложения могут подключать локальное воспроизведение или просмотра элементов audio, video и img к удаленным устройствам.

    Работа ведется с помощью API ) вместе с методами расширений, добавленных к медиа-элементам. Если, например, вы хотите запустить процесс передачи данных на целевой объект воспроизведения, непосредственно вызывая пользовательский интерфейс, выполните следующую последовательность действий:

  • ) :
  • getForCurrentView возвращает объект.
  • showPlayToUI запускает всплывающий элемент интерфейса, где пользователь выбирает приемник.
  • событие sourceRequested, когда пользователь выбирает приемник.
  • В sourceRequested
  • Получить объект ) из элемента audio, video, или img (свойство msPlayToSource) и передать в e.setSource.
  • Установить свойство PlayToSource.next на msPlayToSource другого элемента для непрерывного воспроизведения.
  • Принять событие медиа-элемента ended для воспроизведения дополнительных мультимедийных данных
  • Другой подход, как показано в примере "Проигрывание мультимедиа на целевых устройствах воспроизведения" (http://code.msdn.microsoft.com/windowsapps/Media-PlayTo-Sample-fedcb0f9), идет дальше и проигрывает мультимедийные файлы локально, затем позволяя пользователю выбрать целевое устройство воспроизведения из меню чудо-кнопки Устройства. В подобном случае вам не нужно ничего предпринимать, так как Windows возьмет текущий элемент, выполняющий воспроизведение, и соответствующим образом направит вывод. Но приложение может прослушивать событие ), которое будет вызвано, когда пользователь выберет целевое устройство и когда происходят другие изменения.

    На самом деле, функция воспроизведения на целевом устройстве предназначена для передачи потоковых данных на медиа-проигрыватели, которые могут быть подключены к телевизору или к другим большим экранам. С помощью этого механизма вы можете выбрать локальное содержимое на устройстве, которое работает под управлением Windows 8 и отправить его прямо на проигрыватель, способный принимать потоковые данные. Но, кроме того, подобный приемник можно реализовать программно, то есть, создать приложение, которое может принимать потоковое содержимое из PlayTo-источника. В примере "Приемник PlayTo" (http://code.msdn.microsoft.com/windowsapps/PlayToReceiver-sample-607f00ed) реализован такой подход, и если вы запустите его на другом компьютере в вашей локальной сети, он будет показан в интерфейсе чудо-кнопки Устройства следующим образом:

    Вы даже можете запустить это приложение с основного рабочего компьютера, воспользовавшись возможностями удаленной отладки Visual Studio, что позволит вам пошагово исполнить и код приложения-источника, и код приложения-приемника в одно и то же время! (Другая возможность – это запустить Windows Media Player на одном компьютере, установить параметр Разрешить удаленное управление проигрывателем (Allow Remote Control of My Player) в меню Поток (Stream). В результате данный компьютер появится в списке целевых устройств функции Воспроизвести на (PlayTo)).

    Для того чтобы выполнять функции приемника, приложению обычно нужно объявить некоторые дополнительные сетевые возможности в манифесте, а именно - Интернет (клиент и сервер) (Internet (ClientServer)), и Частные сети (клиент и сервер) (Private Networks (ClientServer)), иначе приложение не будет работать так, как нужно. Затем приложение создает экземпляр Windows.Media.PlayTo.PlayToReceiver, как показано в функции startPlayToReceiver (js/audiovideoptr.js), в примере "Приемник PlayTo":

    function startPlayToReceiver() {	
    if (!g_receiver) {	
    g_receiver = new Windows.Media.PlayTo.PlayToReceiver();
    }	
     

    Далее, нужно подключить обработчики для элементов, которые будут воспроизводить мультимедиа-поток:

    var dmrVideo = id("dmrVideo");
    dmrVideo.addEventListener("volumechange", g_elementHandler.volumechange, false); dmrVideo.addEventListener("ratechange", 
    g_elementHandler.ratechange, false); dmrVideo.addEventListener("loadedmetadata", g_elementHandler.loadedmetadata, false); 
    dmrVideo.addEventListener("durationchange", g_elementHandler.durationchange, false); dmrVideo.addEventListener("seeking", 
    g_elementHandler.seeking, false); dmrVideo.addEventListener("seeked", g_elementHandler.seeked, false); dmrVideo.addEventListener
    ("playing", g_elementHandler.playing, false); dmrVideo.addEventListener("pause", g_elementHandler.pause, false); 
    dmrVideo.addEventListener("ended", g_elementHandler.ended, false);
    dmrVideo.addEventListener("error", g_elementHandler.error, false);
     

    А так же – обработчики для событий, которые вызывает объект приемника:

    g_receiver.addEventListener("playrequested", g_receiverHandler.playrequested, false); 
    g_receiver.addEventListener("pauserequested", g_receiverHandler.pauserequested, false); 
    g_receiver.addEventListener("sourcechangerequested", g_receiverHandler.sourcechangerequested, false); 
    g_receiver.addEventListener("playbackratechangerequested", g_receiverHandler.playbackratechangerequested, false);
    g_receiver.addEventListener("currenttimechangerequested", g_receiverHandler.currenttimechangerequested, false);
    g_receiver.addEventListener("mutechangerequested",
    g_receiverHandler.mutedchangerequested, false); g_receiver.addEventListener("volumechangerequested", 
    g_receiverHandler.volumechangerequested, false); g_receiver.addEventListener("timeupdaterequested",
    g_receiverHandler.timeupdaterequested, false); g_receiver.addEventListeer("stoprequested", 
    g_receiverHandler.stoprequested, false); g_receiver.supportsVideo = true;
    g_receiver.supportsAudio = true;
    g_receiver.supportsImage = false;
    g_receiver.friendlyName = 'SDK JS Sample PlayToReceiver';
     

    Последняя строка выше, как вы можете понять по вышеприведенному изображению, это строка, которая отобразится в интерфейсе чудо-кнопки Устройства для данного приложения-приемника, как только оно станет доступным в сети. Это выполняется путем вызова startAsync:

    // Анонсирует приемник в локальной сети и начинает прием команд
    g_receiver.startAsync().then(function () {
    g_receiverStarted = true;
    
    // Предотвращает блокировку экрана	
    if (!g_displayRequest) {	
    g_displayRequest = new Windows.System.Display.DisplayRequest();
    }	
    g_displayRequest.requestActive();	
    });	
     

    Среди всех событий объекта-приемника, наиболее важное – sourcechanderequested, где eventArgs.stream содержит мультимедиа-данные, которые мы хотим проиграть в выбранном элементе. Это просто реализуется путем создания большого двоичного объекта из потока, и затем URI из этого объекта, которое мы можем присвоить атрибуту src элемента:

    sourcechangerequested: function (eventIn) {
    if (!eventIn.stream) {
    id("dmrVideo").src = "";
    } else {
    var blob = MSApp.createBlobFromRandomAccessStream(eventIn.stream.contentType, eventIn.stream);
    id("dmrVideo").src = URL.createObjectURL(blob, {oneTimeOnly: true});
    }
    }
     

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

    Что мы только что изучили

  • Мультимедиа-элементы сожно создать в разметке или в коде, используя стандартные элементы img, svg, canvas, audio и video.
  • Три графических элемента – img, svg и canvas – могут выводить изображения, выглядящие одинаково, но обладающие разными характеристиками, касающиеся их создания и того, как они масштабируются. Все они, однако, могут быть стилизованы с помощью CSS.
  • Объект Windows.System.Display.DisplayRequest предназначен для отключения скринсейверов и экрана блокировки в течение проигрывания видео (или в других подходящих случаях).
  • И аудио- и видеоэлементы предоставляют множество расширений API (свойств, методов и событий) для работы с различными специфическими возможностями платформы Windows 8, таких, как горизонтальное отражение, масштабирование, оптимизация воспроизведения, 3D-видео, воспроизведение с низкой задержкой, воспроизведение на целевых устройствах, управление воспроизведением различных типов или категорий аудиозаписей, эффекты (обычно реализуемые в виде DLL в пакете приложения), и управление цифровыми правами.
  • Фоновое аудио поддерживается для различных категорий, для реализации этой возможности нужны объявления возможностей в манифесте, и обработчики для событий элементов управления воспроизведения (таким образом, звук может быть соответствующим образом воспроизводиться и приостанавливаться). События элементов управления мультимедиа так же важны для поддержки пользовательского интерфейса управления воспроизведением.
  • Посредством API WinRT приложения могут управлять богатым набором метаданных и свойств мультимедиа-файлов, в том числе – эскизами, изображениями альбомов и свойствами, специфичными для типа содержимого, в том числе, они имеют доступ к очень большому списку свойств Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977%28v=vs.85%29.aspx).
  • API WinRT предоставляет средства для декодирования и кодирования мультимедиа-файлов и потоков, с помощью которых содержимое может быть конвертированы, могут быть изменены свойства содержимого. Это включает в себя поддержку пользовательских кодеков.
  • WinRT предоставляет богатое возможностями API для захвата мультимедиа (фотографий, видеоклипов, аудиозаписей) в том числе, встроенный пользовательский интерфейс для этих целей, вместе с возможностью реализовать собственный, а так же – возможности по перечислению имеющихся устройств и по доступу к ним.
  • Поддерживается воспроизведение потокового мультимедиа с сервера (с применением управления цифровыми правами, или без них, и с возможностью использовать PlayReady), между приложениями (как работа с входящими данными, так и с исходящими), и с приложений на целевые устройства воспроизведения (PlayTo). Приложение, кроме того, может быть настроено в качестве PlayTo-приемника.
  • Страницы:

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

    WinRT поддерживает эти действия посредством простого API в пространстве имен ), используя форматы WPL (Windows Media Player), ZPL (Zune) и M3U. Пример "Список воспроизведения" (http://code.msdn.microsoft.com/windowsapps/Playlist-sample-3d80daee) в Windows SDK (который почти победил в конкурсе на самое короткое название примера!) показывает, как выполнять различные задачи с данным API. В Сценарии 1 он позволяет вам выбрать несколько файлов с использованием средства выбора файлов, создает новый объект Windows. ), добавляет выбранные файлы в его список files (вектор объектов StorageFile) и сохраняет список воспроизведения с помощью метода saveAsAsync (этот код из create.js упрощен и слегка переформатирован):

     
    function pickAudio() {	
    var picker = new Windows.Storage.Pickers.FileOpenPicker();	
    picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.musicLibrary;
    picker.fileTypeFilter.replaceAll(SdkSample.audioExtensions);	
    
    picker.pickMultipleFilesAsync().done(function (files) {
    if (files.size > 0) {
    SdkSample.playlist = new Windows.Media.Playlists.Playlist();
    
    files.forEach(function (file) { SdkSample.playlist.files.append(file);
    });
    
    SdkSample.playlist.saveAsAsync(Windows.Storage.KnownFolders.musicLibrary,
    "Sample", Windows.Storage.NameCollisionOption.replaceExisting,	
    Windows.Media.Playlists.PlaylistFormat.windowsMedia)	
    .done();	
    }	
    }	
    

    Обратите внимание на то, что saveAsAsync принимает объект типа StorageFolder и имя для файла (вместе с необязательным параметром формата). Это соответствует обычно используемому шаблону для списков воспроизведения, где у музыкального приложения есть отдельная папка, где оно хранит списки воспроизведения и предоставляет пользователю простые средства для их именования и/или выбора. При таком подходе списками воспроизведения обычно управляют не так как пользовательскими файлами, когда действия выполняются с помощью средства выбора файлов для выполнения команды Сохранить как (Save as) в произвольной папке. Вы можете использовать FileSavePicker, получить StorageFile, и использовать его свойство path для получения подходящего объекта StorageFolder, но вероятнее всего, вы будете сохранять списки воспроизведения в одном месте и представлять их в виде сущностей, которые появляются лишь внутри самого приложения.

    Например, приложение Музыка (Music), поставляемое вместе с Windows 8, позволяет вам создавать новый список воспроизведения, когда вы просматриваете записи некоторого альбома. Следующие команды появляются на панели приложения (слева), и когда вы выбираете команду Новый список воспроизведения (New playlist), появляется всплывающий элемент (в середине), запрашивающий имя, после чего всплывающий элемент появляется на панели приложения (справа):

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

    Для загрузки плейлиста используется метод Playlist.loadAsync, которому передается StorageFile для плейлиста. Это может быть объект StorageFile, полученный от средства выбора файлов, или из перечисления выделенной для списков воспроизведения папки приложения. Сценарий 2 примера "Список воспроизведения" (display.js) показывает последний подход, после чего осуществляется обход файлов и запрос их музыкальных свойств:

    
    function displayPlaylist() {	
    var picker = new Windows.Storage.Pickers.FileOpenPicker();	
    picker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.musicLibrary;
    picker.fileTypeFilter.replaceAll(SdkSample.playlistExtensions);	
    
    var promiseCount = 0;
    
    picker.pickSingleFileAsync()
    .then(function (item) {
    if (item) {
    return Windows.Media.Playlists.Playlist.loadAsync(item);
    }
    return WinJS.Promise.wrapError("No file picked.");
    })
    .then(function (playlist) { SdkSample.playlist = playlist;
    var promises = {};
    
    // Запрос музыкальных свойств для каждого файла в списке.	
    playlist.files.forEach(function (file) {	
    promises[promiseCount++] = file.properties.getMusicPropertiesAsync();
    });	
    
    // Вывод музыкальных свойств каждого файла. По причине асинхронной
    // природы вызова по получению музыкальных свойств, данные могут появиться
    // в ином порядке, нежели они заданы в исходном списке воспроизведения.
    // Для обеспечения верного порядка, мы используем Promise.join с ассоциативным массивом
    // переданным в качестве параметра, содержащим индекс каждого отдельного promise.
    return WinJS.Promise.join(promises);	
    })	
    .done(function (results) {	
    var output = "Playlist content:\n\n";	
    
    var musicProperties;	
    for (var resultIndex = 0; resultIndex < promiseCount; resultIndex++) {
    musicProperties = results[resultIndex];	
    output += "Title: " + musicProperties.title + "\n";	
    output += "Album: " + musicProperties.album + "\n";	
    output += "Artist: " + musicProperties.artist + "\n\n";	
    }	
    
    if (resultIndex === 0) {
    output += "(playlist is empty)";
    }
    
    }, function (error) {
    // ...
    });
    }
     

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

    Другой метод управления плейлистами – это PlayList.saveAsync, который принимает единственный StorageFile. Такой подход используется, когда вы загружаете и модифицируете список воспроизведения и просто хотите сохранить эти изменения (что обычно выполняется автоматически, когда пользователь добавляет или удаляет элементы плейлиста). Это показано в Сценариях 3, 4 и 5 примера (add.js, js/remove.js и js/clear.js), где просто использованы методы вектора Playlist.files, наподобие append, removeAtEnd и clear, соответственно.

    Проигрывание списка воспроизведения, конечно, зависит от типов включенных в него элементов, но обычно загружают плейлист и последовательно получают объекты StorageFile из его вектора files, передают их в URL.createObjectURL, и затем присваивают полученный URI атрибуту src элемента audio или video. Вы можете, кроме того, использовать плейлисты для управления списками изображений для слайд-шоу

    Загрузка мультимедийных данных и управление ими

    Пользователь может хранить медиафайлы везде, но изображения, музыка и видео обычно хранятся в соответствующих библиотеках пользователя. Проще говоря, это папки, которые медиа-приложениям следует использовать по умолчанию до тех пор, пока пользователь не укажет иную папку с помощью средства выбора папок. Как мы видели в лекции 2, приложения могут объявлять о программном доступе к библиотекам изображений, музыки и видео в манифестах, и получать объекты StorageFolder для них посредством Windows.Storage.KnownFolders:

    var picsLib = Windows.Storage.KnownFolders.picturesLibrary;
     var musicLib = Windows.Storage.KnownFolders.musicLibrary;
     var vidsLib = Windows.Storage.KnownFolders.videosLibrary;
     

    Приложения для работы с фотографиями обычно объявляют возможность Библиотека изображений (Pictures Library), и отображают ее содержимое в ListView. Приложения для работы с музыкой и видео так же используют соответствующие библиотеки, как вы можете видеть во встроенных приложениях Windows 8 Фотографии (Photos), Музыка (Music) и Видео (Video). Помните так же, что если вы забудете объявить соответствующие возможности, строки кода выше приведут к выдаче исключения "Доступ запрещен". Вы тут же поймете, если вы забудете эти важные особенности.

    Я должен предупредить вас заранее, что работа с мультимедиа может быть очень сложной и запутанной. Поэтому вы, возможно, сочтете полезным просмотреть несколько материалов в документации, таких, как "Обработка файлов изображений" (http://msdn.microsoft.com/library/windows/apps/hh465103.aspx), "Перекодировка" (http://msdn.microsoft.com/ru-ru/library/windows/apps/hh452806.aspx), "Применение расширений мультимедиа" (http://msdn.microsoft.com/library/windows/apps/Hh700365.aspx).

    Метаданные мультимедийных файлов

    Когда у нас есть StorageFolder для некоторой медиабиблиотеки или некоторый набор таких объектов, мы можем использовать, как мы уже видели в лекции 2, их метод getItemAsync для получения их содержимого. Так же вы можете использовать файловые запросы для перечисления файлов, которые подпадают под некоторый критерий. В любом случае, в итоге вы получаете коллекцию объектов storageFile, с которой вы можете работать так, как вам нужно.

    Теперь начинается кое-что интересное. Как я упоминал в лекции 2, вы можете получать дополнительные метаданные для этих файлов. Имеется несколько уровней данных, которые вы будете исследовать, начиная открывать тайные двери класса StorageFile, как показано на рис. 6.1. Следующий раздел, в свою очередь, посвящем этим областям. (рис 6.1) Взаимосвязь между объектом StorageFile и другими объектами, получаемыми с его помощью

    Эскизы

    Во-первых, вызов StorageFile.getThumbnailAsync предоставляет изображение эскиза, соответствующее конкретному "режиму" из перечисления ). Здесь есть такие варианты, как ), который предоставляет данные эскиза в виде потока. Вы можете удобно передать его нашему старому другу URL.createObjectURL для отображения в элементе img и так далее.

    Примеры этого найти в примере "Эскизы файлов и папок" (http://code.msdn.microsoft.com/windowsapps/File-thumbnails-sample-17575959). Сценарий 1, например, (js/scenario1.js), предоставляет эскиз и отображает его в элементе img:

    file.getThumbnailAsync(thumbnailMode, requestedSize, thumbnailOptions).done(function (thumbnail) {
    if (thumbnail) {	
    outputResult(file, thumbnail, modeNames[modeSelected], requestedSize);	
    }	
    // ...	
    });	
    
    function outputResult(item, thumbnailImage, thumbnailMode, requestedSize) {	
    document.getElementById("picture-thumb-imageHolder").src = URL.createObjectURL(thumbnailImage,
    { oneTimeOnly: true });	
    // ...	
    }	
     

    Общие свойства файлов

    Общие свойства файлов – это те свойства, которые имеются у всех файлов – находятся во множестве различных мест. Наиболее общие свойства можно обнаружить непосредственно в объекте StorageFile, наподобие attributes, contentType, dateCreated, displayName, displayType, fileType, name, и path.

    Свойства следующей группы можно получить из ), который содержит свойства dateModified, itemDate, и size. "Вот скукота!", скажете вы себе. Ну хорошо, у этого объекта еще есть дополнительный метод, который называется retrievePropertiesAsync, который предоставляет вам массив пар имя – значение для разных штуковин.

    Весь фокус в том, чтобы понять, что вы можете взять массив имен свойств, которые вам нужны, где каждое имя – это строка, происходящая из очень длинного списка свойств Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977.aspx), таких, как System.FileOwner и System.FileAttributes, и передать его ), который мы видели в лекции 2:

     var dateAccessedProperty = "System.DateAccessed";
    var fileOwnerProperty   = "System.FileOwner";
    
    SdkSample.sampleFile.getBasicPropertiesAsync().then(function (basicProperties) { 
    outputDiv.innerHTML += "Size: " + basicProperties.size + " bytes<br />"; 
    outputDiv.innerHTML += "Date modified: " + basicProperties.dateModified + "<br />";
    
    // Получить дополнительные свойства
    return SdkSample.sampleFile.properties.retrievePropertiesAsync([fileOwnerProperty, dateAccessedProperty]);
    }).done(function (extraProperties) {
    var propValue = extraProperties[dateAccessedProperty];
    if (propValue !== null) {
    outputDiv.innerHTML += "Date accessed: " + propValue + "<br />";
    }
    propValue = extraProperties[fileOwnerProperty];
    if (propValue !== null) {
    outputDiv.innerHTML += "File owner: " + propValue;
    }
    });
    

    Что здесь особенно удобно, так это то, что здесь вы можете получить почти любое свойство, которое вам нужно (список свойств содержит тысячи позиций), затем модифицировать массив и вызвать BasicProperties.savePropertiesAsync. Вуаля! Только что вы обновили эти свойства файлов. Вариация savePropertiesAsync так же позволяет вам передавать специальный массив из пар имя – значение, если вы лишь хотите изменить лишь некоторые из свойств.

    Третий набор свойств можно найти, если пройти через тайную дверь ), методы которого retrievePropertiesAsync и savePropertiesAsync похожи на те, что мы видели у BasicProperties. Гораздо интереснее то, что у него есть еще четыре метода: getDocumentPropertiesAsync, getImagePropertiesAsync, getMusicPropertiesAsync, и getVideoPropertiesAsync. С их помощью вы можете получить по-настоящему специальные сведения для конкретных типов файлов, как мы увидим дальше.

    Специальные свойства мультимедиа

    Вместе с классом BasicProperties в пространстве имен Windows.Storage.FileProperties мы так же можем найти классы, возвращаемые методами StorageFile.properties.get*PropertiesAsync: ImageProperties, VideoProperties, MusicProperties, и DocumentProperties. Хотя мы и так довольно далеко забрались, чтобы их найти, каждый из них, по-настоящему глубоко, содержит несметные информационные сокровища. Таблицы ниже, в свою очередь, приводят сводные данные по ним. Обратите внимание на то, что объект каждого типа содержит метод retrievePropertiesAsync, похожий на соответствующий метод BasicProperties, который позволяет вам запрашивать дополнительные свойства по имени, которые не включены в свойства основного объекта. Воспользуйтесь ссылками в верхней части таблицы для того, чтобы воспользоваться справочными разделами, которые позвлят обнаружить наиболее подходящие свойства Windows.

    ImageProperties из StorageFile.properties.getImagePropertiesAsync
    Дополнительные свойства System.Image, System.Photo, System.Media
    Свойство Тип данных Соответствующее свойство Windows
    title String System.Title
    dateTaken Date System.Photo.DateTaken
    latitude Double (смотрите ниже) System.GPS.LatitudeDecimal, мли комбинация System.GPS.Latitude, System.GPS.LatitudeDenominator, System.GPS.LatitudeNumerator, and System.GPS.LatitudeRef
    longitude Double (смотрите ниже) System.GPS.LongitudeDecimal, или комбинация System.GPS.Longitude, System.GPS.LongitudeDenominator, System.GPS.LongitudeNumerator, and System.GPS.LongitudeRef
    cameraManufacturer String System.Photo.CameraManufacturer
    cameraModel String System.Photo.CameraModel
    width Количество в пикселях System.Image.HorizontalSize
    height Количество в пикселях System.Image.VerticalSize
    orientation Windows.Storage.FileProperties.- PhotoOrientation содержит unspecified, normal, flipHorizontal, flipVertical, transpose, transverse, rotate90, rotate180, rotate270 System.Photo.Orientation
    peopleNames String vector System.Photo.PeopleNames
    keywords String vector System.Keywords
    rating Число (1-99 с 0 означающим "нет оценки") System.Rating
    VideoProperties из StorageFile.properties.getVideoPropertiesAsync
    Дополнительные свойства System.Video, System.Media, System.Image, System.Photo
    Свойство Тип данных Соответствующее свойство Windows
    title String System.Title
    subtitle String System.Media.SubTitle
    year Number System.Media.Year
    publisher String System.Media.Publisher
    rating Number System.Rating
    width Number in pixels System.Video.FrameWidth
    height Number in pixels System.Video.FrameHeight
    orientation Windows.Storage.FileProperties.- VideoOrientation содержит normal, rotate90, rotate180, rotate270 System.Photo.Orientation
    duration Число (в 100-нс единицах, т.е. 1/10th миллисекунды) System.Media.Duration
    bitrate Число (в битах в секунду) System.Video.TotalBitrate, System.Video.EncodingBitrate
    directors String vector System.Video.Director
    producers String vector System.Media.Producer
    writers String vector System.Media.Writer
    keywords String vector System.Keywords
    latitude Double (смотрите ниже) System.GPS.LatitudeDecimal, или комбинация System.GPS.Latitude, System.GPS.LatitudeDenominator, System.GPS.LatitudeNumerator, and System.GPS.LatitudeRef
    longitude Double (смотрите ниже) System.GPS.LongitudeDecimal, или комбинация System.GPS.Longitude, System.GPS.LongitudeDenominator, System.GPS.LongitudeNumerator, and System.GPS.LongitudeRef
    MusicProperties из StorageFile.properties.getMusicPropertiesAsync
    Дополнительные свойства System.Music, System.Media
    Свойство Тип данных Соответствующее свойство Windows
    title String System.Title, System.Music.AlbumTitle
    subtitle String System.Media.SubTitle
    trackNumber Number System.Music.TrackNumber
    year Number System.Media.Year
    publisher String System.Media.Publisher
    artist String System.Music.Artist, System.Music.DisplayArtist
    albumArtist String System.Music.DisplayArtist (чтение), System.Music.AlbumArtist (запись)
    genre String vector System.Music.Genre
    composers String vector System.Music.Composer
    conductors String vector System.Music.Conductor
    rating Число (1-99 с 0 означающим "нет оценки") System.Rating
    duration Число (в 100-нс единицах, т.е. 1/10th миллисекунды System.Media.Duration
    bitrate Число (в битах в секунду) System.Video.TotalBitrate, System.Video.EncodingBitrate
    producers String vector System.Media.Producer
    writers String vector System.Media.Writer
    DocumentProperties from
    Дополнительные свойства System
    Свойство Тип данных Соответствующее свойство Windows
    title String System.Title
    Author String vector System.Author
    keywords String vector System.Keywords
    Comments String System.Comment

    Пара замечаний обо всем этом. Во-первых, переменные String vector (строковые векторы) – это, как мы видели ранее, экземпляры ), который предоставляет методы для манипуляции, наподобие append, insertAt, removeA, и так далее. в JavaScript вы можете получить доступ к членам вектора как к массиву, через квадратные скобки [ ]; следует лишь помнить, что доступные методы более специфичны.

    Во-вторых, свойства ) (в js/default.js) содержит вспомогательную функцию для извлечения компонентов этих значений и конвертирования их в строку:

    "convertLatLongToString": function (latLong, isLatitude) {
    var reference;
    
    if (isLatitude) {	
    reference = (latLong >= 0) ? "N" : "S";
    } else {	
    reference = (latLong >= 0) ? "E" : "W";
    }	
    
    latLong = Math.abs(latLong);	
    var degrees = Math.floor(latLong);	
    var minutes = Math.floor((latLong - degrees) * 60);	
    var seconds = ((latLong - degrees - minutes / 60) * 3600).toFixed(2);
    return degrees + "°" + minutes + "\'" + seconds + "\"" + reference;
    }
     

    Подводя итоги, знак величины указывает на направление. Положительное значение для широты (latitude) указывает на Север, отрицательное означает Юг, для долготы (longitude) положительное значение означает Восток, отрицательное – Запад. Целая часть числа представляет собой градусы, дробная содержит значение минут, за базу взято 60. Умножение этого значения на 60 дает полное количество минут, то, что остается, содержит секунды. Это может показаться странным, но подобные исходные данные получают от GPS-приемника и API определения местоположения обычно конвертирует их.

    Мультимедийные свойства в примерах

    Несколько примеров из Windows SDK показывают, как работать с некоторыми из вышеописанных свойств и как работать со свойствами в общем. Пример "Простая работа с изображениями" (http://code.msdn.microsoft.com/windowsapps/Simple-Imaging-Sample-a2dec2b0), в Сценарии 1, предоставляет наиболее полную демонстрацию, так как вы можете выбрать файл изображения и он загрузит и отобразит различные свойства, как показано на рис. 6.2 (я прокрутил список вниз, чтобы показать полный набор свойств). Я могу подтвердить, что дата, производитель и модель камеры, сведения об экспозиции, верны. (рис 6.2) Свойства файла изображения в примере "Простая работа с изображениями"

    Метод примера openHandler получает эти свойства из файла, в частности, выполняя вызов StorageFile.properties.getImagePropertiesAsync и используя метод ImageProperties.retrievePropertiesAsync для пары дополнительных свойств уже не в ImageProperties. Затем getImagePropertiesForDisplay объединяет их в единый объект, используемый для вывода данных в пользовательский интерфейс примера. Некоторые строки кода, приведенного ниже, опущены.

    var ImageProperties = {};
    
    function openHandler() {
    // Хранит данные между асинхронными вызовами. 
    var file = {};
    
    Helpers.getFileFromOpenPickerAsync().then(function (_file) {
    file = _file;	
    return file.properties.getImagePropertiesAsync();	
    }).then(function (imageProps) {	
    ImageProperties = imageProps;	
    
    var requests = [	
    "System.Photo.ExposureTime",    // В секундах	
    "System.Photo.FNumber"    // Значение диафрагмы из данных EXIF
    };	
    
    return ImageProperties.retrievePropertiesAsync(requests);	
    }).done(function (retrievedProps) {	
    // Форматирование свойств в виде текста для отображения в интерфейсе.	
    displayImageUI(file, getImagePropertiesForDisplay(retrievedProps));
    });	
     }	
    
    function getImagePropertiesForDisplay(retrievedProps) {
    // Если заданное свойство не существует, его значением будет null.
    var orientationText = Helpers.getOrientationString(ImageProperties.orientation);
    
    var exposureText = retrievedProps.lookup("System.Photo.ExposureTime") ?
    retrievedProps.lookup("System.Photo.ExposureTime") * 1000 + " ms" : "";
    
    var fNumberText = retrievedProps.lookup("System.Photo.FNumber") ?
    retrievedProps.lookup("System.Photo.FNumber").toFixed(1) : "";
    
    // Опущено: код для конвертации ImageProperties.latitude и ImageProperties.longitude в
    // градусы, минуты, секунды, и направление
    
    return {
    "title": ImageProperties.title,
    "keywords": ImageProperties.keywords, // строковой массив
    "rating": ImageProperties.rating, // число 
    "dateTaken": ImageProperties.dateTaken, 
    "make": ImageProperties.cameraManufacturer, 
    "model": ImageProperties.cameraModel, 
    "orientation": orientationText,
    // Опущено: свойства широты/долготы
    "exposure": exposureText, 
    "fNumber": fNumberText
    };
    }
     

    Функция displayImageUI, которой передаются эти свойства, в основном просто копирует данные в различные элементы управления. Полезно будет снова отметить, однако, что отображение фотографии реализовано с помощью нашего хорошего друга URL.createObjectURL:

    function displayImageUI(file, propertyText) {
    id("outputImage").src = window.URL.createObjectURL(file, { oneTimeOnly: true });
     

    Небольшой пример работы с ), как мы уже видели в разделе "Списки воспроизведения". Вы можете сейчас вернуться назад и взглянуть на код, приведенный в этом разделе, и вы вполне сможете понять, что происходит. В SDK нет примеров использования VideoProperties и DocumentProperties, работа с ними выполняется по тем же шаблонам, как показано выше для ImageProperties, поэтому написание необходимого кода не вызовет затруднений.

    Кроме того, взглянем еще раз на пример "Конфигурация кнопок для мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-Buttons-ea57d8e2), который мы видели ранее, в разделе "Пользовательский интерфейс управления воспроизведением". Он показывает, как использовать музыкальные свойства для того, чтобы получить изображение альбома.

    Что касается сохранения свойств, пример "Простая работа с изображениями" демонстрирует это в Сценарии 1. Так как поля, показаные ранее, на рис. 6.2, можно редактировать, в примере есть кнопка Apply (Применить), которая вызывает функцию applyHandler, показанную ниже, для записи измененных свойств в файл:

    function applyHandler() {
    ImageProperties.title = id("propertiesTitle").value;
    
    // Ключевые слова хранятся в строковом массиве.
     Разделение строк знаками перевода строки.
    ImageProperties.keywords.clear();	
    if (id("propertiesKeywords").value !== "") {	
    var keywordsArray = id("propertiesKeywords").value.split("\n");	
    
    keywordsArray.forEach(function (keyword) {	
    ImageProperties.keywords.append(keyword);
    });	
    }	
    
    var properties = new Windows.Foundation.Collections.PropertySet();
    
    // При записи оценок, используйте ключ свойства "System.Rating".	
    // ImageProperties.rating не обрабатывает значение оценки 0 (нет звезд/оценок).
    properties.insert("System.Rating", Helpers.convertStarsToSystemRating(	
    id("propertiesRatingControl").winControl.userRating	
    ));	
    
    // Код опущен: конвертирование дискретных значений широты и
     долготы из пользовательского интерфейса в
    // форму, подходящую для свойства, и проверка данных; 
    конечный результат этих манипуляций
    // сохраняется в списке свойств
    properties.insert("System.GPS.LatitudeRef", latitudeRef); properties.insert("System.GPS.LongitudeRef", longitudeRef); 
    properties.insert("System.GPS.LatitudeNumerator", latNum); properties.insert("System.GPS.LongitudeNumerator", longNum); 
    properties.insert("System.GPS.LatitudeDenominator", latDen);
    properties.insert("System.GPS.LongitudeDenominator", longDen);
    
    // Запись массива свойств в файл
    
    ImageProperties.savePropertiesAsync(properties).done(function () {	
    // ...	
    }, function (error) {	
    // Обработка некоторых ошибок и некоторые свойства могут
     не поддерживаться всеми форматами изображений.
    });	
    }	
     

    Несколько примечательных особенностей этого кода перечислены ниже:

  • Он разделяет ключевые слова в элементе управления пользовательского интерфейса и отдельно добавляет каждое к вектору в свойстве keywords.
  • Он создает новую коллекцию свойств типа Windows.Foundation.Collections.PropertySet и использует ее метод insert для добавления свойств в список. Этот набор свойств – именно то, чего ожидает метод savePropertiesAsync.
  • Метод ) особо описывает подобное преобразование.
  • В итоге, вся детализированная информация, которая нужна вам для любого конкретного свойства Windows, может быть найдена на справочной странице этого свойства. Начать углубленное изучение свойств можно со страницы Свойства Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977.aspx).

    Манипуляция изображениями и их кодирование

    Для того, чтобы сделать с изображениями нечто большее, чем загрузка и отображение (где вы можете применить различные CSS-трансформации для реализации эффектов), вам нужно получить доступ к пикселям с помощью декодера (decoder). Это уже происходит внутри механизмов вывода изображения, когда вы присваиваете URI img.src, но для того, чтобы получить непосредственный доступ к пикселям, нужно выполнить ручное декодирование изображения. С другой стороны, сохранение пиксельной информации в виде файла требует использования кодера.

    WinRT предоставляет API и для того, и для другого в пространстве имен ), а именно, в классах BitmapDecoder, BitmapTransform, и BitmapEncoder. Загрузка, выполнение манипуляций с изображением и сохранение его в файл часто включает в себя использование всех трех классов, хотя объект BitmapTransform предназначен для поворота и масштабирования, таким образом, вы не будете пользоваться им, если выполняете другие манипуляции.

    Демонстрацию этого API можно найти в Сценарии 2 примера "Простая работа с изображениями" (http://code.msdn.microsoft.com/windowsapps/Simple-Imaging-Sample-a2dec2b0). Я оставляю вам этот код для того, чтобы вы просмотрели его в редакторе, так как он довольно сложен – до 11 promise-вызовов, объединенных в цепочку для сохранения файла! Так же он выполняет декодирование, манипуляцию и кодирование внутри одной функции, такой, как saveHandler (js/scenario2.js). Вот какую последовательность действий реализует этот код:

  • Открыть файл с помощью StorageFile.openAsync, получив поток.
  • Передать поток статическому методу BitmapDecoder.createAsync, что позволит получить конкретный экземпляр BitmapDecoder для потока.
  • Переать декодер статическому методу BitmapEncoder.createForTranscodingAsync, который предоставляет экземпляр BitmapEncoder. Этот кодировщик создается с помощью InMemoryRandomAccessStream.
  • Установить свойства в свойстве кодировщика bitmapTransform property (объект BitmapTransform) для настройки масштабирования и поворота. Это позволит создать трансформированное изображение в потоке в памяти.
  • Создать набор свойств (Windows.Graphics.Imaging.BitmapPropertySet), который включает в себя System.Photo.Orientation и использует команду кодера bitmapProperties.setPropertiesAsync для того, чтобы это сохранить.
  • Копирование потока из памяти в выходной файловых поток с использованием Windows.Storage.Stream.RandomAccessStream.copyAsync.
  • Закрытие обоих потоков с помощью соответствующих методов (тем самым закрывается файл).
  • В сложных сценариях, подобных этому, полезно рассмотреть отдельные стадии этого процесса, для этой цели в материалах к этой лекции есть упражнение ImageManipulation. Он позволит вам выбрать и загрузить изоражение, конвертировать его в оттенки серого и сохранить конвертированное изображение в новый файл. Результат работы показан на рис. 6.3. Кроме того, это дает вам возможность, как мы можем отправлять декодированное изображение в элемент HTML canvas и сохранять содержимое элемента в виде файла. (рис 6.3) Результаты работы упражнения ImageManipulation из дополнительных материалов к лекции

    Обработчик кнопки Load Image (Загрузить изображение) (loadImage в js/default.js) предоставляет первоначальное отображение. Он позволяет вам выбирать изображение с помощью средства вабра файлов, отображает полноразмерное изображение в элементе img с помощью URL.createObjectURL, вызывает StorageFile.properties.getImagePropertiesAsync для получения свойств title и dateTaken, и применяет StorageFile.getThumbnailAsync для получения эскиза, который отображается в верхней части. Мы уже видели все эти API в действии.

    При щелчке на кнопке Grayscale (Оттенки серого), мы входим в обработчик setGrayscale, где происходят интересные действия. Мы вызываем StorageFile.openReadAsync для того, чтобы получить поток, вызываем BitmapDecoder.createAsync с этим потоком для того, чтобы получить декодер, кэшируем некоторые подробности из декодера в локальном объекте (encoding), вызываем BitmapDecoder.getPixelDataAsync и копируем эти пиксели в элемент canvas (и здесь лишь три асинхронных операции, объединенных в цепочку!):

     var Imaging = Windows.Graphics.Imaging; //Короткое имя	
    var imageFile;            //Полученный из средства выбора файла	
    var decoder;           //Полученный из BitmapDecoder.createAsync	
    var encoding = {};        //Для кэширования некоторых деталей из декодера
    
    function setGrayscale() {
    //Декодируем файл изображения в пиксельные данные для canvas
    
    //Получаем входной поток длф файла (объект StorageFile полученный после открытия)
    imageFile.openReadAsync().then(function (stream) {	
    //Создаем декодер, используя статический метод createAsync и поток файла
    return Imaging.BitmapDecoder.createAsync(stream);	
    }).then(function (decoderArg) {	
    decoder = decoderArg;	
    
    //Настраиваем декодер, если хотим. 
    Параметры по умолчанию, это BitmapPixelFormat.rgba8 и
    //BitmapAlphaMode.ignore. 
    Параметризованная версия getPixelDataAsync может так же
    //управлять трансформацией, а так же ExifOrientationMode, 
    и ColorManagementMode если нужно.
    
    //Кэшируем эти параметры для кодирования	
    encoding.dpiX = decoder.dpiX;	
    encoding.dpiY = decoder.dpiY;	
    encoding.pixelFormat = decoder.bitmapPixelFormat;
    encoding.alphaMode = decoder.bitmapAlphaMode;	
    encoding.width = decoder.pixelWidth;	
    encoding.height = decoder.pixelHeight;	
    
    return decoder.getPixelDataAsync();
    }).done(function (pixelProvider) {
    //detachPixelData получает реальные пиксели (массив нельзя вернуть из 
    //асинхронной операции)
    copyGrayscaleToCanvas(pixelProvider.detachPixelData(), decoder.pixelWidth, decoder.pixelHeight);
    });
    }
    

    Метод декодера ) существует в двух формах. Более простая форма, показанная здесь, выполняет декодирование с использованием настроек по умолчанию. Версия, которую можно полностью контролировать, позволяет вам задать другие параметры, как пояснено в комментариях к вышеприведенному коду. Обычное использование этого метода ведет к выполнению трансформации с использованием объекта Windows.Graphics.Imaging.BitmapTransform (как упомянуто выше), что включает в себя масштабирование (с различными моделями интерполяции), поворот (с приращением в 90 градусов), обрезку и отражение.

    В любом случае, то, что вы получаете из ), единственный восхитительный синхронный метод которого detachPixelData дает вам необходимый массив. (И этот метод можно вызвать лишь один раз, он не будет работать при последующих вызовах, отсюда и имя "detach" (отсоединить)). В итоге мы получаем данные, которые нам нужны для манипуляции пикселями и отображения результатов в элементе canvas, что и демонстрирует функция copyGrayscaleToCanvas. Вы можете, конечно, заменить эту функцию любой подпрограммой для манипуляции пикселями:

    function copyGrayscaleToCanvas(pixels, width, height) {
    //Настраиваем контекст canvas и получаем его пиксельный массив
    var canvas = document.getElementById("canvas1");	
    canvas.width = width;	
    canvas.height = height;	
    var ctx = canvas.getContext("2d");	
    
    //Обходим данные и копируем значения пикселей 
    в canvas после конвертации в оттенки серого
    var imgData = ctx.createImageData(canvas.width, canvas.height);	
    var colorOffset = { red: 0, green: 1, blue: 2, alpha: 3 };	
    var r, g, b, gray;	
    var data = imgData.data; //Очень влияет на производительность!	
    
    for (var i = 0; i < pixels.length; i += 4) {
    r = pixels[i + colorOffset.red];	
    g = pixels[i + colorOffset.green];	
    b = pixels[i + colorOffset.blue];	
    
    //Присваиваем каждое rgb-значение яркости
    gray = Math.floor(.3 * r + .55 * g + .11 * b);
    
    data[i + colorOffset.red] = gray;	
    data[i + colorOffset.green] = gray;	
    data[i + colorOffset.blue] = gray;	
    data[i + colorOffset.alpha] = pixels[i + colorOffset.alpha];
    }	
    
    //Показываем результат в элементе canvas ctx.putImageData(imgData, 0, 0);
    
    //Активируем кнопку сохранения 
    document.getElementById("btnSave").disabled = false;
    }
     

    Это – хороший случай убедиться в том, что JavaScript – это не лучшия язык для работы с большими группами пикселей вроде этой, хотя в данном случае производительность Release build (построения Выпуска), запущенного вне отладчика, весьма неплоха. Подобные подпрограммы лучше реализовывать в виде WinRT-компонентов на языках вроде C# или C++ и делать вызываемыми из JavaScript. У нас будет возможность сделать это в лекции 5 курса "Программная логика приложений для Windows 8, созданных с использованием HTML, CSS и JavaScript и их взаимодействие с системой", где мы так же увидим ограничения элемента canvas, которые требуют несколько иного подхода.

    Сохранение данных из canvas в файл происходит в функции saveGrayscale, где мы используем средство выбора файла для получения StorageFile, открываем поток, берем пиксельные данные из элемента canvas и передаем их BitmapEncoder:

    function saveGrayscale() {	
    var picker = new Windows.Storage.Pickers.FileSavePicker();	
    picker.suggestedStartLocation =	
    Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
    picker.suggestedFileName = imageFile.name + " - grayscale";	
    picker.fileTypeChoices.insert("PNG file", [".png"]);	
    
    var imgData, fileStream = null;
    
    picker.pickSaveFileAsync().then(function (file) {
    if (file) {
    return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
    } else {
    return WinJS.Promise.wrapError("No file selected");
    }
    }).then(function (stream) {
    fileStream = stream;
    var canvas = document.getElementById("canvas1");
    var ctx = canvas.getContext("2d");
    imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    
    return Imaging.BitmapEncoder.createAsync( Imaging.BitmapEncoder.pngEncoderId, stream);
    }).then(function (encoder) {
    //Задаем пиксельные данные, подразумевая, что объект "encoding" 
    имеет необходимые параметры.
    //Конверсия из данных элемента canvas в Uint8Array необходима, 
    так как тип массива из элемента canvas
    //не соответствует тому, что нужно здесь WinRT.
    encoder.setPixelData(encoding.pixelFormat, encoding.alphaMode,
    encoding.width, encoding.height, encoding.dpiX, encoding.dpiY,
    new Uint8Array(imgData.data));
    
    //Начинаем кодирование
    return encoder.flushAsync();
    }).done(function () {
    fileStream.close();
    }, function () {
    //Пустой обработчик ошибки (если пользователь закрыл средство выбора файлов, 
    ничего не происходит)
    });
    }
     

    Обратите внимание на то, как BitmapEncoder принимает идентификатор кодека в первом параметре. Мы используем pngEncoderId, который, как вы можете видеть, определен как статическое свойство класса Windows.Graphics.Imaging.BitmapEncoder; другие возможные значения – это bmpEncoderId, gifEncoderId, jpegEncoderId, jpegXREncoderId, и tiffEncoderId. Все это – форматы, поддерживаемые API. Вы можете установить дополнительные свойства BitmapEncoder прежде чем зададите пиксельные данные, такие, как его BitmapTransform, эти установки будут применены при кодировании.

    Одна особенность, о которой нужно знать, заключается в том, что пиксельный массив, полученный из элемента canvas (тип DOM ) не совместима напрямую с байтовым массивом WinRT, который нужен кодировщику. По этой причине мы используем вызов new Uint8Array в последнем параметре.

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

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

    Во-первых, это метод ), который был кратко упомянут в контексте примера "Простая обработка изображений". С его помощью создается новый кодировщик, который инициализируется на основе существующего объекта ), параметры прозрачности (premultiplied, straight, или ignore, смотрите BitmapAlphaMode ( http://msdn.microsoft.com/library/windows/apps/windows.graphics.imaging.bitmapalphamode.aspx ) размер изображения, DPI изображения, и, конечно, сами пиксельные данные. Помимо этого, вы можете изменить другие свойства посредством метода кодировщика ) (ничего себе имя метода!). Кодировщик позволяет вызывать лишь bitmapProperties.setPropertiesAsync, bitmapProperties. getPropertiesAsync, и flushAsync, и так как это подразумевает неизменность исходных данных, это выполняется быстрее, чем при использовании его более гибкого эквивалента и требует меньше памяти.

    Кодировщик из createForTranscodingAsync не приспособлен для изменения формата файла изображения (например, JPEG на PNG); для этого вам нужно использовать createAsync, где вы можете задать конкретный тип кодирования. Как мы уже видели, первый аргумент createAsync это идентификатор кодека, куда обычно передают одно из статических свойств из Windows.Graphics.Imaging.BitmapEncoder. Я еще не упоминал о том, что вы можете так же задавать пользовательские кодеки в этом первом параметре, и что createAsync может так же поддерживать необязательный третий аргумент, в котором можно передать параметры для конкретного кодека. Однако здесь имеются некоторые сложности и ограничения.

    Позвольте мне сначала остановиться на параметрах. Существующая документация по значениям кодеков BitmapEncoder (наподобие pngEncoderId) не содержит многих подробностей о доступных параметрах. Для того, чтобы их узнать, вам нужно обратиться к документации по Windows Imaging Component (WIC), в частности, к материалу Native WIC Codecs (http://msdn.microsoft.com/library/windows/desktop/gg430027.aspx), который посвящен тому, что WinRT предоставляет приложениям для Магазина Windows. Если вы пройдете на страницу конкретного кодека, вы увидите раздел "Encoder Options" (Свойства кодера), который содержит описания того, что можно использовать. Например, кодек JPEG (http://msdn.microsoft.com/library/windows/desktop/gg430026.aspx) поддерживает свойства наподобие ) поддерживает свойства наподобие FilterOption для различных оптимизаций сжатия файлов.

    Для того, чтобы задать эти свойства, вам нужно создать новый BitmapPropertySet и вставить в этот набор запись для каждого необходимого параметра. Если, например, у вас есть переменная, содержащая данные о качестве изображения, которое вы хотите использовать при кодировании JPEG, вы можете создать кодер, таким образом:

    var options = new Windows.Graphics.Imaging.BitmapPropertySet();	
    options.insert("ImageQuality", quality);	
    var encoderPromise = Imaging.BitmapEncoder.createAsync(Imaging.BitmapEncoder.jpegEncoderId,
    stream, options);	
     

    Вы используете тот же самый BitmapPropertySet для любых свойств, которые вы можете передать при вызове метода кодера bitmapProperties.setPropertiesAsync. Здесь мы используем тот же механизм для свойств кодировщика.

    Что касается пользовательских кодеков, это просто означает, что первый аргумент ). Загвоздка здесь в том, что включение пользовательских кодеков для изображений в пакет приложения не поддерживается. Если кодек уже присутствует в системе (то есть, установлен традиционным образом), это будет работать. Однако, политика Магазина Windows не позволяет одним приложениям зависеть от других, так что маловероятно, что вы сможете хотя бы отправить подобное приложение, если только оно не предустановлено на каком-то конкретном OEM-устройстве и нужная DLL не является частью образа системы (Приложения, написанные на C++ могут большее в да нной области, но это выходит за границы данного курса).Короче говоря, в случае с приложениями, написанными на JavaScript и HTML, вы, в работе с различными форматами изображений, по-настоящему ограничены применением кодеков, которые изначально поддерживаются системой.

    Заметьте, что эти ограничения не применяются для пользовательских аудио- и видеокодеков. Пример "Расширения мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-extensions-sample-7b466096) показывает, как работать с пользовательским видеокодеком, что мы увидим в следующем разделе.

    Манипуляция аудио и видео

    Как и при работе с изображения, если все, что нам нужно – это загрузить содержимое StorageFile в аудио- или видеоэлемент, мы можем просто передать этот StorageFile в URL.createObjectUrl и пприсвоить результат атрибуту src. Похожим образом, если нам нужны исходные данные, мы можем просто использовать методы StorageFile.openAsync или openReadAsync для получения файлового потока.

    Честно говоря, открытие файла, это, вероятнее всего, самое сложное, что вам придется делать в JavaScript с исходными аудио- или видеоданными, если вообще придется этим пользоваться. В то время, как процесс обработка изображений находится где-то на грани приемлемости в окружении JavaScript, обрабатывать аудио, и особенно видео, лучше всего в высокопроизводительных DLL C++. На самом деле, легко доступны множесто платформенно нейтральных библиотек C++ сторонних разработчиков для подобных целей, которые вы сможете легко включить в состав подобных DLL. В подобном случае вы можете просто позволить DLL открыть нужный файл.

    Тем не менее, WinRT предоставляет средства для перекодирования (конвертации) различных мультимедиа-форматов и предоставляет модель расширения для пользовательских кодеков, эффектов и обработчиков схем. На самом деле, мы уже видели, как применять пользовательские видео эффекты в примере "Расширения мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-extensions-sample-7b466096), и те же DLL можно использовать при кодировании, где все, что выполняет код JavaScript – это связывание вместе нужных компонентов (что он делает очень хорошо). Посмотрим как это делается, сначала на примере перекодирования видео, а затем – на примере пользовательского кодека.

    Перекодирование

    Перекодирование аудио и видео реализуется посредством класса ), который поддерживает выходные форматы mp3 и wma для аудио, и mp4, wmv и m4a для видео. Процесс перекодирования позволяет вам применять эффекты, осуществлять обрезку композиций.

    Перекодирование происходит либо из одного объекта ). Для настройки операции перекодирования, вы вызываете методы ) или ). Он представляет собой операцию, которая готова к работе, но обработка не начинается, пока вы не вызовете его метод transcodeAsync. В JavaScript, подобный результат является promise-объектом, который позволяет вам предоставлять обработчики завершения и выполнения для отдельной операции, а так же позволяет комбинировать операции с помощью WinJS.Promise.join. Это позволяет сначала настроить операции, а потом запустить их выполнение, что полезно для пакетной обработки и выполнения автоматической отправки на сервисы наподобие YouTube, пока вы спите! (В подобное время я вытаскиваю упаковку льда из морозилки и кладу ее под мой ноутбук из-за его плохой системы охлаждения)

    Пример "Перекодирование мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-Transcode-Sample-f7ba5269) показывает нам пару сценариев перекодирования. В Сценарии 1 (js/presets.js) мы можем выбрать видеофайл, задать целевой формат, выбрать профиль перекодирования и предоставить компьютеру возможность сделать свою работу (информация о продвижении операции предоставляется), как показано на рис. 6.4. (рис 6.4) Пример перекодирования мультимедиа обрабатывает клип с моим двухлетним сыном, который исследует измерительную рулетку

    Код, который выполняется, когда вы нажимаете на кнопку Transcode (Перекодировать) приведен ниже (кое-что опущено; в примере использованы вложенные promise-вызовы, что не рекомендуется, если вам нужна обработка ошибок, этот код поглощает любые исключения, которые происходят до вызова transcodeAsync):

    function onTranscode() {	
    // Создадим объект для перекодирования.	
    var transcoder = null;	
    transcoder = new Windows.Media.Transcoding.MediaTranscoder();
    
    // Получим профиль перекодирования.
    getPresetProfile(id("profileSelect"));
    
    // Создадим выходной файл и начнем перекодировку.	
    var videoLib = Windows.Storage.KnownFolders.videosLibrary;	
    var createFileOp = videoLib.createFileAsync(g_outputFileName,	
    Windows.Storage.CreationCollisionOption.generateUniqueName);
    
    createFileOp.done(function (ofile) {	
    g_outputFile = ofile;	
    g_transcodeOp = null;	
    var prepareOp = transcoder.prepareFileTranscodeAsync(g_inputFile, g_outputFile,
    g_profile);	
    
    prepareOp.done(function (result) {
    if (result.canTranscode) {
    g_transcodeOp = result.transcodeAsync();
    g_transcodeOp.done(transcodeComplete, transcoderErrorHandler, transcodeProgress);
    } else {
    transcodeFailure(result.failureReason);
    }
    }); // prepareOp.done 
    id("cancel").disabled = false;
    }); // createFileOp.done
    }
     

    Метод getPresentProfile получает подходящий объект профиля, соответствующий параметрам, выбранным в приложении. Для параметров, выбранных на рис. 6.4 (WMV и WVGA) мы используем следующие части этой функции:

    function getPresetProfile(profileSelect) {	
    g_profile = null;	
    var mediaProperties = Windows.Media.MediaProperties;
    var videoEncodingProfile;	
    
    switch (profileSelect.selectedIndex) {
    // Остальные варианты опущены 
    case 2:
    videoEncodingProfile = mediaProperties.VideoEncodingQuality.wvga;
    break;
    }
    if (g_useMp4) {
    g_profile = mediaProperties.MediaEncodingProfile.createMp4(videoEncodingProfile);
    } else {
    g_profile = mediaProperties.MediaEncodingProfile.createWmv(videoEncodingProfile);
    }
    }
     

    В Сценарии 2 всегда используется WVGA-коодирование, но здесь вы можете установить необходимые значения для размерностей видео, частоты кадров, битрейта аудио и видео, настроить аудиоканалы и аудиосэмплинг. Данные параметры используются в getCustomProfile (js/custom.js), просто конфигурируя параметры профиля после его создания:

    function getCustomProfile() {	
    if (g_useMp4) {	
    g_profile = Windows.Media.MediaProperties.MediaEncodingProfile.createMp4(
    Windows.Media.MediaProperties.VideoEncodingQuality.wvga);	
    } else {	
    g_profile = Windows.Media.MediaProperties.MediaEncodingProfile.createWmv(
    Windows.Media.MediaProperties.VideoEncodingQuality.wvga);	
    }	
    
    // Берем значения настроек из элементов пользовательского интерфейса 
    g_profile.audio.bitsPerSample = id("AudioBPS").value; 
    g_profile.audio.channelCount = id("AudioCC").value; 
    g_profile.audio.bitrate = id("AudioBR").value; 
    g_profile.audio.sampleRate = id("AudioSR").value; 
    g_profile.video.width = id("VideoW").value; 
    g_profile.video.height = id("VideoH").value; 
    g_profile.video.bitrate = id("VideoBR").value; 
    g_profile.video.frameRate.numerator = id("VideoFR").value; 
    g_profile.video.frameRate.denominator = 1;
    }
     

    И, в завершении, Сценарий 3 похож на Сценарий 1, но позволяет вам задать время начала и конца записи для ее обрезки, которые затем сохраняются в свойствах перекодировщика trimStartTime и trimStopTime (js/trim.js):

    transcoder = new Windows.Media.Transcoding.MediaTranscoder();
    transcoder.trimStartTime = g_start;
    transcoder.trimStopTime = g_stop;
     

    Хотя это и не показано в примре, вы можете применять эффекты при операции перекодирования, используя методы перекодировника addAudioEffect и addVideoEffect.

    Пользовательские кодеры/декодеры и обработчики схем

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

    Как было сказано ранее, о пользовательских форматах изображений, эти вопросы ведут в весьма обширную область вокруг Windows Media Foundation (WMF) SDK (http://msdn.microsoft.com/library/ms694197.aspx). И WinRT лишь предоставляет оболочку для этого, поэтому изучение WMF существенно в этих вопросах, и это занятие не для слабонервных!

    Аудио- и видеорасширения объявляются в манифесте приложения, вам понадобится непосредственно править XML. Как можно увидеть в примере "Расширения мультимедиа" (http://code.msdn.microsoft.com/windowsapps/Media-extensions-sample-7b466096), для каждой DLL в решении объявление выглядит так:

     <Extension Category="windows.activatableClass.inProcessServer">
    <InProcessServer>	
    <Path>MPEG1Decoder.dll</Path>	
    <ActivatableClass ActivatableClassId="MPEG1Decoder.MPEG1Decoder"
    ThreadingModel="both" />	
    </InProcessServer>	
    </Extension>	
    

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

    В зависимости от использования расширения, вам может понадобиться зарегистрировать его в WinRT посредством методов Windows.Media.MediaExtensionManager: registerAudio[Decoder | Encoder], registerByteStreamHandler (потоки мультимедиа), registerSchemeHandler (источники мультимедиа/файлы-контейнеры), и registerVideo[Decoder | Encoder] . В сценарии 1 примера о расширениях мультимедиа (js/LocalDecoder.js), мы можем видеть, как задать пользовательский декодер для воспроизведения видео.

     воспроизведения видео.
    var page = WinJS.UI.Pages.define("/html/LocalDecoder.html", {	
    extensions: null,	
    MFVideoFormat_MPG1: { value: "{3147504d-0000-0010-8000-00aa00389b71}" },
    NULL_GUID: { value: "{00000000-0000-0000-0000-000000000000}" },	
    
    ready: function (element, options) {
    if (!this.extensions) {
    // Добавьте здесь любой код инициализации
    this.extensions = new Windows.Media.MediaExtensionManager();
    // Зарегистрируем пользовательский ByteStreamHandler и пользовательский декодер. 
    this.extensions.registerByteStreamHandler("MPEG1Source.MPEG1ByteStreamHandler",
    ".mpg", null);
    this.extensions.registerVideoDecoder("MPEG1Decoder.MPEG1Decoder", this.MFVideoFormat_MPG1, this.NULL_GUID);
    }
    
    // ...
    

    Здесь CLSID MPEG1Source.MPEG1ByteStreamHandler реализован в одной DLL (смотрите проект C++ MPEG1Source в решении), а CLSID MPEG1Decoder.MPEG1.Decoder реализован в другой (C++ проект MPEG1Decoder).

    Сценарий 2, в свою очередь, показывает использование пользовательского обработчика схемы, где обработчик (в проекте C++ GeometricSource) генерирует видеокадры в процессе выполнения программы. Притягательная возможность, но находящаяся за пределами тем данного курса.

    Эффекты, как мы можем видеть, довольно просто использовать, если у вас есть реализация эффекта, просто передайте ее CLSID методу наподобие msInsertVideoEffect и msInsertAudioEffect видео или аудиоэлемента. Так же вы можете применить эффект в процессе перекодирования, в методах addAudioEffect и addVideoEffect класса MediaTranscoder. То же самое справедливо и для процесса захвата мультимедийных данных, который мы сейчас рассмотрим.

    Захват мультимедийных данных

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

    И бывают ситуации, когда мы хотим включить камеру или микрофон и что-то записать: сделать фотографию, аудиозапись или видеоклип. Конечно, приложения не могут знать заранее, какие именно камеры и микрофоны могут быть установлены в системе. Ключевой шаг в захвате мультимедийных данных заключается в определении устройства для использования – а это то, что красиво предоставляет нам API Windows.Media.Capture, вместе со средствами для выполнения захвата в файл, поток или в какой-нибудь пользовательский объект, в зависимости от того, как приложение хочет обрабатывать захваченные данные или манипулировать ими.

    В лекции 2 курса "Введение в разработку приложений для Windows 8 с использованием HTML, CSS и JavaScript", в приложении "Here My Am!", мы видели, как просто WinRT позволяет делать фотографии. Нам лишь нужно объявить возможность Веб-камера (Webcam) в манифесте и добавить несколько строк кода:

    function capturePhoto() {
    var that = this;
    
    var captureUI = new Windows.Media.Capture.CameraCaptureUI();
    
    //Показывает, что мы хотим захватить PNG-файл, размером не больше, чем целевй элемент --	
    //пользовательский интерфейс автоматически выведет рамку, обрезающую изображение под этот размер	
    captureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
    captureUI.photoSettings.croppedSizeInPixels =	
    { width: this.clientWidth, height: this.clientHeight };	
    
    captureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo)
    .done(function (capturedFile) {
    //Убедитесь, что проверили, подходит ли вам возвращенный элемент: он может содержать null, если
    //пользователь отменил действие. 
    if (capturedFile) {
    lastCapture = capturedFile; //Сохраним для целей общего доступа
    that.src = URL.createObjectURL(capturedFile, {oneTimeOnly: true});
    }
    }, function (error) {
    console.log("Unable to invoke capture UI.");
    });
    }
     

    Пользовательский интерфейс, который Windows предоставляет нам посредством данного API, предоставляет настройки для обрезки и повторной съемки изображения, и для установки параметров камеры. Другой пример получения фотографии можно найти в Сценарии 1 примера "Пользовательский интерфейс камеры" (http://code.msdn.microsoft.com/windowsapps/CameraCaptureUI-Sample-845a53ac), вместе с примером захвата видео в Сценарии 2. В последнем случае (js/capturevideo.js) мы настраиваем объект интерфейса захвата данных на видеоформат и указываем видеорежим при вызове captureFileAsync:

    function captureVideo() {
    var dialog = new Windows.Media.Capture.CameraCaptureUI();
    dialog.videoSettings.format = Windows.Media.Capture.CameraCaptureUIVideoFormat.mp4;
    
    dialog.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.video)
    .done(function (file) {
    if (file) {
    var videoBlobUrl = URL.createObjectURL(file, {oneTimeOnly: true});
    } else {
    //...
    }
    }, function (err) {
    //...
    });
    }
     

    Следует отметить, что возможность Веб-камера (Webcam) в манифесте применима лишь для захвата фотографий или записи видео без звука. Если вы хотите захватывать аудиосигнал, убедитесь в том, что объявили возможность Микрофон (Microphone) на закладке Возможности (Capabilities) редактора манифеста.

    Если вы взглянете на объект ), который включает в себя сведения по управлению вызовами на устройстве связи Bluetooth.

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

    Все эти возможности доступны посредством класса ):

    Свойства Описание (классы в пространстве имен Windows.Media.Capture если не указано иное)
    audioController Свойство AudioDeviceController, которое управляет громкостью и предоставляет возможность управлять другими свойствами, воздействующими на аудиопоток.
    mediaCaptureSettings Свойство MediaCaptureSettings, содержажее ID устройств и параметры режима, и позволяет устанавливать источник (audio, videoPreview, photo).
    videoController Свойство VideoDeviceController, которое управляет свойствами изображения (яркостью, оттенком, панорамированием/наклоном, масштабом и так далее), предоставляя настройки для учета задней подсветки объекта и частоты переменного тока, и предоставляет возможность управления другими свойствами, воздействующими на видеопоток.
    События Описание
    failed Вызывается, когда в поцессе захвата происходит ошибка.
    recordLimitationExceeded Вызывается, когда пользователь пытается записать звук или видео, длительность которых превышает разрешенную.
    Методы Описание
    initializeAsync Инициализирует объект MediaCapture (с параметрами по умолчанию или с использованием объекта MediaCaptureInitializationSettings, который содержит то же самое, что содержится в MediaCaptureSettings).
    addEffectAsync Применяет эффект.
    clearEffectsAsync Удаляет все текущие эффекты.
    capturePhotoToStorageFileAsync capturePhotoToStreamAsync Захватывает изображение в файл или в поток с произвольным доступом. Оба варианта принимают экземпляр ), как описано ранее, в разделе "Общие свойства файлов"
    getEncoderProperty setEncoderProperty Управляет специфическими свойствами кодера.
    startRecordToStorageFileAsync startRecordToStreamAsync stopRecordAsync Начинают и останавливают запись в файл или в поток, используя MediaEncodingProfile, который определяет формат аудио или видео, вместе с битрейтом, качеством, размерами видео и так далее.
    getRecordRotation setRecordRotation Применимы для видео, работают со значением VideoRotation (с 90-градусным приращением) в применении к записи. Не воздействуют на аудио.
    startRecordToCustomSinkAsync Начинает запись в пользовательский объект, который описан либо в виде реализации Windows.Media.IMediaExtension либо как ID, с использованием набора параметров.
    startPreviewAsync startPreviewToCustomSinkAsync stopPreviewAsync getPreviewRotation setPreviewRotation Выполняют те же функции, что и при записи, но применяются для целей предварительного просмотра. В данном случае, если вы вызовете URL.createObjectURL и передадите объект MediaCapture в качестве перевого параметра, полученный результат может быть присвоен атрибуту src элемента video и данные предварительного просмотра будут отображены в этом элементе, когда вы вызовете его метод video.play.
    getPreviewMirroring setPreviewMirroring Управляют зеркальным отражением при предварительном просмотре, что подразумевает горизонтальное отражение. Это применимо для камер с различной установкой, которые могут быть направлены в сторону от пользователя (камеры, устанавливаемые на задней панели, как на планшетных компьютерах), или в его сторону (камеры, которые монтируют на мониторах или встраивают в экраны ноутбуков). Смотрите следующий раздел: "Выбор устройства захвата мультимедиа".

    Для того, чтобы увидеть очень простую демонстрацию возможностей предварительного просмотра видео в элементе video, мы можем обратиться к примеру "Параметры пользовательского интерфейса камеры" (http://code.msdn.microsoft.com/windowsapps/CameraOptionsUI-Sample-44c06873), к его файлу js/showoptionsui.js. Когда вы нажимаете на кнопку Start Preview (Начать предварительный просмотр), создается и инициализируется объект MediaCapture:

    function initializeMediaCapture() {	
    mediaCaptureMgr = new Windows.Media.Capture.MediaCapture();	
    mediaCaptureMgr.initializeAsync().done(initializeComplete, initializeError);
     }	
     

    Где обработчик initializeComplete вызывается в startPreivew:

    function startPreview() {	
    document.getElementById("previewTag").src = URL.createObjectURL(mediaCaptureMgr);
    document.getElementById("previewTag").play();	
    startPreviewButton.disabled = true;	
    showSettingsButton.style.visibility = "visible";	
    previewStarted = true;
    }
     

    В этом коде так же показан вызов Windows.Media.Capture.CameraOptionsUI , который происходит, когда вы нажимаете на кнопку Show Settings (Показать параметры), результат показан на рис. 6.5. Это всплывающий элемент, предоставленный системой с параметрами, которые соответствуют захватываемому потоку мультимедиа:

     function showSettings() {	
    if (mediaCaptureMgr) {	
    Windows.Media.Capture.CameraOptionsUI.show(mediaCaptureMgr);
    }	
    }	
    

    Кстати, если у вас возникают проблемы с запуском примера, подобного этому, в имитаторе Visual Studio, в частности, если вы сталкиваетесь с исключением, когда пытаетесь включить камеру – попытайтесь, вместо этого, запустить его на локальном компьютере или на удаленной машине. (рис 6.5) Пользовательский интерфейс настроек параметров камеры из примера (пустая нижняя часть обрезана)

    Более сложный сценарий, включающий в себя использование класса ), в частности, это предварительный просмотр и захват видео, динамическое изменение свойств (Сценарий 1), выбор конкретного мультимедиа-устройства (Сценарий 2) и запись звука, без видео (Сценарий 3).Начнем со Сценария 3 (js/AudioCapture.js, он самый простой), здесь присутствует базовый код для создания и инициализации объекта MediaCapture для аудиопотока (смотрите свойство streamingCaptureMode в параметрах инициализации), где поток направляется в файл в библиотеке музыки посредством startRecordToStorageFileAsync (некоторая часть кода опущена для краткости):

    var mediaCaptureMgr = null;	
    var captureInitSettings = null;
    var encodingProfile = null;	
    var storageFile = null;	
    
    // Это вызывается при загрузке страницы	
    function initCaptureSettings() {	
    captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
    captureInitSettings.audioDeviceId = "";	
    captureInitSettings.videoDeviceId = "";	
    captureInitSettings.streamingCaptureMode =	
    Windows.Media.Capture.StreamingCaptureMode.audio;
     }	
    
    function startDevice() {
    mediaCaptureMgr = new Windows.Media.Capture.MediaCapture();
    
    mediaCaptureMgr.initializeAsync(captureInitSettings).done(function (result) {
    // ...	
    });	
     }	
    
    function startRecord() {
    // ...
    // Начинаем запись. Windows.Storage.KnownFolders.videosLibrary.createFileAsync("cameraCapture.m4a",
    Windows.Storage.CreationCollisionOption.generateUniqueName)
    .done(function (newFile) {
    storageFile = newFile;
    encodingProfile = Windows.Media.MediaProperties
    .MediaEncodingProfile.createM4a(Windows.Media.MediaProperties
    .AudioEncodingQuality.auto);
    mediaCaptureMgr.startRecordToStorageFileAsync(encodingProfile, storageFile).done(function (result) {
    // ...
    });
    });
    }
    
    function stopRecord() {
    mediaCaptureMgr.stopRecordAsync().done(function (result) {
    displayStatus("Record Stopped. File " + storageFile.path + " ");
    
    // Воспроизведение записанного аудио	
    var audio = id("capturePlayback" + scenarioId);	
    audio.src = URL.createObjectURL(storageFile, { oneTimeOnly: true });
    audio.play();	
    });	
     }	
    
     

    Сценарий 1, в целом, использует тот же код, но захватывает видеопоток и фотографии, результат его работы показан на рис. 6.6. Эти особенности включены посредством следующих свойств в установках инициализации (смотрите js/BasicCapture.js, внутри initCaptureSettings):

    captureInitSettings.photoCaptureSource =	
    Windows.Media.Capture.PhotoCaptureSource.videoPreview;	
    captureInitSettings.streamingCaptureMode =	
    Windows.Media.Capture.StreamingCaptureMode.audioAndVideo;
     

    (рис 6.6) Предварительный просмотр и запись видео с устройства по умолчанию в Сценарии 1 примера о захвате мультимедиа. (Изображение обрезано, так как мне понадобилось запустить этот пример с использованием опции Локальный компьютер в Visual Studio, и я не думаю, что вам надо видеть скриншот размером 1920х1200 с большим количеством пустого пространства!)

    Обратите внимание на элементы управления Contrast (Контраст) и Brightness (Яркость) на рис. 4.9. Их изменение меняет видео во время предварительного просмотра, вместе с записываемым видео. В примере это реализовано с помощью свойств contrast и brightness объекта MediaCapture.videoDeviceController, демонстрируя возможность динамической настройки этих (и других) свойств контроллера. Обратитесь к функции getCameraSettings в js/BasicCapture.js, которая подключает события change ползунка в обычной анонимной функции для обновления необходимого свойства.

    Выбор устройства захвата мультимедиа

    Взглянем теперь на Сценарий 2 (js/AdvancedCapture.js), он более или менее похож на Сценарий 1, но он позволяет вам выбирать конкретное устройство захвата. До настоящего момента мы пользовались лишь устройством по умолчанию, но вы не ограничены этим, конечно. Вы можете использовать API ) для получения списка устройств в классе интерфейсов устройств. В примере использован заранее определенный класс videoCapture:

    function enumerateCameras() {
    var cameraSelect = id("cameraSelect");
    deviceList = null;
    deviceList = new Array();
    while (cameraSelect.length > 0) {
    cameraSelect.remove(0);
    }
    //Перечисляем вебкамеры и добавляем их в список
    var deviceInfo = Windows.Devices.Enumeration.DeviceInformation;
    deviceInfo.findAllAsync(Windows.Devices.Enumeration.DeviceClass.videoCapture)
    .done(function (devices) {
    // Добавляем устройства в deviceList 
    if (devices.length > 0) {
    for (var i = 0; i < devices.length; i++) {
    deviceList.push(devices[i]);
    cameraSelect.add(new Option(deviceList[i].name), i);
    }
    //Выбираем первую вебкамеру
    cameraSelect.selectedIndex = 0; 
    initCaptureSettings();
    } else {
    // блокируем кнопки.
    }
    }, errorHandler);
    }
     

    Выбранные ID устройств затем копируются внутри initCaptureSettings в свойство MediaCaptureInitializationSetting.videoDeviceId:

     var selectedIndex = id("cameraSelect").selectedIndex; 
    var deviceInfo = deviceList[selectedIndex]; 
    captureInitSettings.videoDeviceId = deviceInfo.id;
    

    Кстати, вы всегда можете получить ID устройства по умолчанию, воспользовавшись методами объекта ) и прослушивая его события на предмет изменения устройств по умолчанию. Важно отметить, что DeviceInformation (в переменной deviceInfo выше) включает в себя свойство, которое называется enclosureLocation. Оно сообщает о том, направлена ли камера на пользователя, или от него, что вы можете использовать для поворота видео или фото так, чтобы их отображение соответствовало восприятию пользователя:

    var cameraLocation = null;
    
    if (deviceInfo.enclosureLocation) {
    cameraLocation = deviceInfo.enclosureLocation.panel;
    }
    
    if (cameraLocation === Windows.Devices.Enumeration.Panel.back) {
    rotateVideoOnOrientationChange = true;
    reverseVideoRotation = false;
    } else if (cameraLocation === Windows.Devices.Enumeration.Panel.front) {
    rotateVideoOnOrientationChange = true;
    reverseVideoRotation = true;
    } else {
    rotateVideoOnOrientationChange = false;
    }
     

    Сценарий 2 демонстрирует, так же, использование MediaCapture.addEffectAsync с эффектом перевода в оттенки серого, что показано на рис. 6.7., который реализован в DLL (проект GrayscaleTransform в решении). Это работает в точности так, как работало уже рассмотренное нами перекодирование, и вы можете обратиться к функциям addRemoveEffect и addEffectToImageStream в файле примера js/AdvancedCapture.js для того, чтобы увидеть подробности. Вы можете здесь заметить, что эти функции выполняют некоторое количество проверок с использованием значения MediaCaptureSettings.videoDeviceCharacteristic для того, чтобы убедиться, что эффект добавлен в верное место. (рис 6.7) Сценарий 2 примера о захвате мультимедиа, где можно выбирать конкретное устройство захвата и применять эффект (Окно обрезано из скриншота большего размера). Вы достаточно внимательно посмотрели на этот рисунок, чтобы заметить, что я поменял гитары?

    Потоковое мультимедиа и целевые объекты воспроизведения

    Грубым преуменьшением будет говорить, что потоковое мультимедиа популярно в наши дни. Как я упоминал во введении к лекции, один только Netflix занимает немалую долю полосы пропускания современного Интернета (и в моем доме – тоже). YouTube тоже не стоит в стороне – и ваше приложение так же может внести свой вклад в это дело!

    Передача мультимедийных данных с сервера приложению это простой и наболее распространенный случай. Это происходит автоматически, когда вы устанавливаете атрибут ) (он имеет статус бета-версии, когда я это пишу), который призван помочь вам создавать мультимедиа-приложения с большим набором богатых возможностей, включая живое воспроизведение и защиту содержимого PlayReady. Я не описываю в данном курсе этот SDK, я лишь хочу, чтобы вы о нем знали.

    На последних страницах данной лекции мы рассмотрим соображения по управлению цифровыми правами (DRM), и поговорим о потоковой передаче данных, но не из сети, а в сеть. Например, о захвате аудио и видеоинформации коммуникационным приложением, и о передаче приложением потокового мультимедиа на целевые устройства воспроизведения (PlayTo).

    Потоковая передача данных с сервера и DRM

    Потоковая передача мультимедиа с сервера, это то, что вы уже делали, применяя элементы audio и video с URI, указывающими на удаленные ресурсы. Все это выполняется автоматически. На самом деле, все, что делает отличный мультимедиа-клиент – это общается с веб-сервисом, получает метаданные и списки элементов, помогает пользователю перемещаться по этой информации и, в конце концов, получает URI, который можно передать в атрибут src элемента audio или video. Затем, как только приложение получает событие canplay, вы можете вызвать метод play элемента для того, чтобы начать воспроизведение.

    Конечно, мультимедийные записи часто защищены DRM, в противном случае содержимое платных сервисов не приносило бы денег владельцам прав! Таким образом, здесь нужен механизм для получения и проверки прав где-то между установкой атрибута src элемента и получением события canplay. К счастью, есть простые средства для того, чтобы это сделать:

    Перед установкой атрибута ) и настраивается его свойство properties.

    Прослушивается событие этого объекта serviceRequested, обработчик которого производит проверку прав и устанавливает флаг завершения, если все верно. (Два других события, о которых я просто хочу упомянуть, это componentloadfailed и rebootneeded.)

    Присвоение средства управления защитой к элементу audio/video с использованием метода расширения ).

    Установка атрибута src. Это вызовет событие serviceRequested для начала обработки DRM, которая предотвращает вызов canplay до тех пор, пока проверка прав не завершится успешно.

    Вы можете обратиться к материалам "Использование подключаемого DRM" (http://msdn.microsoft.com/library/windows/apps/hh452779.aspx), и "Обработка ошибок DRM" (http://msdn.microsoft.com/library/windows/apps/hh452767.aspx) для того, чтобы узнать подробности, здеь приведен минимальный гипотетический пример кода, который все это реализует:

    var video1 = document.getElementById("video1");
    
    video1.addEventListener('error', function () {
    var error = video1.error.msExtendedCode;	
    //...	
    }, false);	
    
    video1.addEventListener('canplay', function () {
    video1.play();
    }, false);
    
    var cpm = new Windows.Media.Protection.MediaProtectionManager();	
    cpm.addEventListener('servicerequested', enableContent, false); //Удалите это позже
    video1.msSetContentProtectionManager(cpm);	
    video1.src = "http://some.content.server.url/protected.wmv";
    
    
    function enableContent(e) {
    if (typeof (e.request) != 'undefined') {
    var req = e.request;	
    var system = req.protectionSystem;	
    var type = req.type;	
    
    //Предпринять необходимые действия на основе системы и типа
    }
    if (typeof (e.completion) != 'undefined') {
    //Запрошенное действие завершено	
    var comp = e.completion;	
    comp.complete(true);	
    }	
    }	
     

    Как конкретно вы проверяете права, зависит от сервиса, из которого вы получаете данные – и это не то, что вам захочется опубликовать!

    Более полная демонстрация работы с DRM находится в примере "Простое использование PlayReady" (http://code.msdn.microsoft.com/windowsapps/Simple-PlayReady-sample-5c1aefaf), которому нужно, чтобы вы скачали и установили Microsoft PlayReady Client SDK (http://visualstudiogallery.msdn.microsoft.com/e02ccac7-f3eb-4b53-b11a-c657d5631483). PlayReady, если вы еще с ним не знакомы, это сервис лицензирования, который предоставляет Microsoft, таким образом, вам не нужно создавать что-то подобное с нуля. PlayReady Client SDK предоставляет дополнительные инструменты и подержку фреймворка для приложений, которые нуждаются в реализации онлайновых и оффлайновых сценариев работы с мультимедиа, таких, как прогрессивная загрузка, загрузка для владения, выдача напрокат и подписка. В дополнение к этому, с использованием этого SDK вам не нужно отправлять ваше приложение для тестирование на соответствие требованиям DRM. В любом случае, вот как пример использования PlayReady настр аивает свое средство защиты содержимого, просто для того, чтобы показать, как API WinRT используются с конкретными идентификаторами DRM-сервисов:

    mediaProtectionManager = new Windows.Media.Protection.MediaProtectionManager();
    mediaProtectionManager.properties["Windows.Media.Protection.MediaProtectionSystemId"] =
    '{F4637010-03C3-42CD-B932-B48ADF3A6A54}'
    
    var cpsystems = new Windows.Foundation.Collections.PropertySet();	
    cpsystems["{F4637010-03C3-42CD-B932-B48ADF3A6A54}"] =	
    "Microsoft.Media.PlayReadyClient.PlayReadyWinRTTrustedInput";	
    mediaProtectionManager.properties[	
    "Windows.Media.Protection.MediaProtectionSystemIdMapping"] = cpsystems;
     

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

    Следующий случай касается ситуации, когда приложение является источником потоковых мультимедиа-данных, вместо того, чтобы быть их потребителем, что означает, что клиентские приложения в других местах выступают в таком качестве. На самом деле, в этом сценарии – аудио- или видеокоммуникации и реализации конференц-связи – обычно приложение играет обе роли, выполняя потоковую передачу данных другим клиентским приложениям и получая данные от них. Так работают Windows Live Messenger, Skype и другие подобные программы, вместе с приложениями наподобие игр, которые включают в себя возможности чата.

    Вот как такие приложения обычно работают:

  • Настройка необходимых каналов связи через сеть, это могут быть одноранговые системы, или системы с привлечением некоей централизованной службы.
  • Захват аудио и видео в поток с использованием API WinRT, которые мы уже видели (в особенности MediaCapture.startRecordToStreamAsync), или захват в пользовательский объект.
  • Выполнение любой дополнительной обработки данных в потоке. Обратите внимание, однако, что здесь применяются эффекты, подключенные к механизму захвата (MediaCapture.addEffectAsync), вместо тех, которые обрабатывают уже захваченные данные.
  • Кодирование потока для передачи любым необходимым способом.
  • Передача потока через сетевой канал связи.
  • Прием передач от других подключенных приложений.
  • Декодирование переданных приложению потоков и конверсия в большой двоичный объект (blob) с использованием MSApp.createBlobFromRandomAccessStream.
  • Использование URL.createObjectURL для подключения элементов audio и video к потоку.
  • Для того, чтобы увидеть это в действии, обратитесь к примеру "Коммуникация в реальном времени" (http://code.msdn.microsoft.com/Simple-Communication-Sample-eac73290), который реализует видеочат в Сценарии 2 и показывает работу с различными режимами задержки с Сценарии 1. Последние два шага в вышеприведенном списке так же показаны в примере "Приемник целевого объекта воспроизведения" (http://code.msdn.microsoft.com/windowsapps/PlayToReceiver-sample-607f00ed), который настроен на прием мультимедиа-потоков из других источников.

    Целевые объекты воспроизведения

    Последний вариант работы с потоковыми данными построен вокруг возможности передачи данных на целевые объекты воспроизведения (PlayTo), которая была представлена в Windows 7. Проще говоря, эта возможность представляет собой средство, с помощью которого приложения могут подключать локальное воспроизведение или просмотра элементов audio, video и img к удаленным устройствам.

    Работа ведется с помощью API ) вместе с методами расширений, добавленных к медиа-элементам. Если, например, вы хотите запустить процесс передачи данных на целевой объект воспроизведения, непосредственно вызывая пользовательский интерфейс, выполните следующую последовательность действий:

  • ) :
  • getForCurrentView возвращает объект.
  • showPlayToUI запускает всплывающий элемент интерфейса, где пользователь выбирает приемник.
  • событие sourceRequested, когда пользователь выбирает приемник.
  • В sourceRequested
  • Получить объект ) из элемента audio, video, или img (свойство msPlayToSource) и передать в e.setSource.
  • Установить свойство PlayToSource.next на msPlayToSource другого элемента для непрерывного воспроизведения.
  • Принять событие медиа-элемента ended для воспроизведения дополнительных мультимедийных данных
  • Другой подход, как показано в примере "Проигрывание мультимедиа на целевых устройствах воспроизведения" (http://code.msdn.microsoft.com/windowsapps/Media-PlayTo-Sample-fedcb0f9), идет дальше и проигрывает мультимедийные файлы локально, затем позволяя пользователю выбрать целевое устройство воспроизведения из меню чудо-кнопки Устройства. В подобном случае вам не нужно ничего предпринимать, так как Windows возьмет текущий элемент, выполняющий воспроизведение, и соответствующим образом направит вывод. Но приложение может прослушивать событие ), которое будет вызвано, когда пользователь выберет целевое устройство и когда происходят другие изменения.

    На самом деле, функция воспроизведения на целевом устройстве предназначена для передачи потоковых данных на медиа-проигрыватели, которые могут быть подключены к телевизору или к другим большим экранам. С помощью этого механизма вы можете выбрать локальное содержимое на устройстве, которое работает под управлением Windows 8 и отправить его прямо на проигрыватель, способный принимать потоковые данные. Но, кроме того, подобный приемник можно реализовать программно, то есть, создать приложение, которое может принимать потоковое содержимое из PlayTo-источника. В примере "Приемник PlayTo" (http://code.msdn.microsoft.com/windowsapps/PlayToReceiver-sample-607f00ed) реализован такой подход, и если вы запустите его на другом компьютере в вашей локальной сети, он будет показан в интерфейсе чудо-кнопки Устройства следующим образом:

    Вы даже можете запустить это приложение с основного рабочего компьютера, воспользовавшись возможностями удаленной отладки Visual Studio, что позволит вам пошагово исполнить и код приложения-источника, и код приложения-приемника в одно и то же время! (Другая возможность – это запустить Windows Media Player на одном компьютере, установить параметр Разрешить удаленное управление проигрывателем (Allow Remote Control of My Player) в меню Поток (Stream). В результате данный компьютер появится в списке целевых устройств функции Воспроизвести на (PlayTo)).

    Для того чтобы выполнять функции приемника, приложению обычно нужно объявить некоторые дополнительные сетевые возможности в манифесте, а именно - Интернет (клиент и сервер) (Internet (ClientServer)), и Частные сети (клиент и сервер) (Private Networks (ClientServer)), иначе приложение не будет работать так, как нужно. Затем приложение создает экземпляр Windows.Media.PlayTo.PlayToReceiver, как показано в функции startPlayToReceiver (js/audiovideoptr.js), в примере "Приемник PlayTo":

    function startPlayToReceiver() {	
    if (!g_receiver) {	
    g_receiver = new Windows.Media.PlayTo.PlayToReceiver();
    }	
     

    Далее, нужно подключить обработчики для элементов, которые будут воспроизводить мультимедиа-поток:

    var dmrVideo = id("dmrVideo");
    dmrVideo.addEventListener("volumechange", g_elementHandler.volumechange, false); dmrVideo.addEventListener("ratechange", 
    g_elementHandler.ratechange, false); dmrVideo.addEventListener("loadedmetadata", g_elementHandler.loadedmetadata, false); 
    dmrVideo.addEventListener("durationchange", g_elementHandler.durationchange, false); dmrVideo.addEventListener("seeking", 
    g_elementHandler.seeking, false); dmrVideo.addEventListener("seeked", g_elementHandler.seeked, false); dmrVideo.addEventListener
    ("playing", g_elementHandler.playing, false); dmrVideo.addEventListener("pause", g_elementHandler.pause, false); 
    dmrVideo.addEventListener("ended", g_elementHandler.ended, false);
    dmrVideo.addEventListener("error", g_elementHandler.error, false);
     

    А так же – обработчики для событий, которые вызывает объект приемника:

    g_receiver.addEventListener("playrequested", g_receiverHandler.playrequested, false); 
    g_receiver.addEventListener("pauserequested", g_receiverHandler.pauserequested, false); 
    g_receiver.addEventListener("sourcechangerequested", g_receiverHandler.sourcechangerequested, false); 
    g_receiver.addEventListener("playbackratechangerequested", g_receiverHandler.playbackratechangerequested, false);
    g_receiver.addEventListener("currenttimechangerequested", g_receiverHandler.currenttimechangerequested, false);
    g_receiver.addEventListener("mutechangerequested",
    g_receiverHandler.mutedchangerequested, false); g_receiver.addEventListener("volumechangerequested", 
    g_receiverHandler.volumechangerequested, false); g_receiver.addEventListener("timeupdaterequested",
    g_receiverHandler.timeupdaterequested, false); g_receiver.addEventListeer("stoprequested", 
    g_receiverHandler.stoprequested, false); g_receiver.supportsVideo = true;
    g_receiver.supportsAudio = true;
    g_receiver.supportsImage = false;
    g_receiver.friendlyName = 'SDK JS Sample PlayToReceiver';
     

    Последняя строка выше, как вы можете понять по вышеприведенному изображению, это строка, которая отобразится в интерфейсе чудо-кнопки Устройства для данного приложения-приемника, как только оно станет доступным в сети. Это выполняется путем вызова startAsync:

    // Анонсирует приемник в локальной сети и начинает прием команд
    g_receiver.startAsync().then(function () {
    g_receiverStarted = true;
    
    // Предотвращает блокировку экрана	
    if (!g_displayRequest) {	
    g_displayRequest = new Windows.System.Display.DisplayRequest();
    }	
    g_displayRequest.requestActive();	
    });	
     

    Среди всех событий объекта-приемника, наиболее важное – sourcechanderequested, где eventArgs.stream содержит мультимедиа-данные, которые мы хотим проиграть в выбранном элементе. Это просто реализуется путем создания большого двоичного объекта из потока, и затем URI из этого объекта, которое мы можем присвоить атрибуту src элемента:

    sourcechangerequested: function (eventIn) {
    if (!eventIn.stream) {
    id("dmrVideo").src = "";
    } else {
    var blob = MSApp.createBlobFromRandomAccessStream(eventIn.stream.contentType, eventIn.stream);
    id("dmrVideo").src = URL.createObjectURL(blob, {oneTimeOnly: true});
    }
    }
     

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

    Что мы только что изучили

  • Мультимедиа-элементы сожно создать в разметке или в коде, используя стандартные элементы img, svg, canvas, audio и video.
  • Три графических элемента – img, svg и canvas – могут выводить изображения, выглядящие одинаково, но обладающие разными характеристиками, касающиеся их создания и того, как они масштабируются. Все они, однако, могут быть стилизованы с помощью CSS.
  • Объект Windows.System.Display.DisplayRequest предназначен для отключения скринсейверов и экрана блокировки в течение проигрывания видео (или в других подходящих случаях).
  • И аудио- и видеоэлементы предоставляют множество расширений API (свойств, методов и событий) для работы с различными специфическими возможностями платформы Windows 8, таких, как горизонтальное отражение, масштабирование, оптимизация воспроизведения, 3D-видео, воспроизведение с низкой задержкой, воспроизведение на целевых устройствах, управление воспроизведением различных типов или категорий аудиозаписей, эффекты (обычно реализуемые в виде DLL в пакете приложения), и управление цифровыми правами.
  • Фоновое аудио поддерживается для различных категорий, для реализации этой возможности нужны объявления возможностей в манифесте, и обработчики для событий элементов управления воспроизведения (таким образом, звук может быть соответствующим образом воспроизводиться и приостанавливаться). События элементов управления мультимедиа так же важны для поддержки пользовательского интерфейса управления воспроизведением.
  • Посредством API WinRT приложения могут управлять богатым набором метаданных и свойств мультимедиа-файлов, в том числе – эскизами, изображениями альбомов и свойствами, специфичными для типа содержимого, в том числе, они имеют доступ к очень большому списку свойств Windows (http://msdn.microsoft.com/library/windows/desktop/dd561977%28v=vs.85%29.aspx).
  • API WinRT предоставляет средства для декодирования и кодирования мультимедиа-файлов и потоков, с помощью которых содержимое может быть конвертированы, могут быть изменены свойства содержимого. Это включает в себя поддержку пользовательских кодеков.
  • WinRT предоставляет богатое возможностями API для захвата мультимедиа (фотографий, видеоклипов, аудиозаписей) в том числе, встроенный пользовательский интерфейс для этих целей, вместе с возможностью реализовать собственный, а так же – возможности по перечислению имеющихся устройств и по доступу к ним.
  • Поддерживается воспроизведение потокового мультимедиа с сервера (с применением управления цифровыми правами, или без них, и с возможностью использовать PlayReady), между приложениями (как работа с входящими данными, так и с исходящими), и с приложений на целевые устройства воспроизведения (PlayTo). Приложение, кроме того, может быть настроено в качестве PlayTo-приемника.
  • Вернуться к учебному плану