Передать на печать

Начала Metro-программирования: мультимедиа (ч.2)

Окончание статьи, посвящённой мультимедийным возможностям платформы Metro.


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

4.1. Поддержка видеофайлов с несколькими звуковыми дорожками
Практически все современные форматы видеофайлов позволяют хранить в одном файле сразу несколько дорожек звука. Обычно эта возможность используется для распространения фильмов со звуковым сопровождением на нескольких языках; каждая звуковая дорожка содержит перевод фильма на один из заявленных языков.

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

Объект HTMLVideoElement поддерживает свойство audioTracks. Оно возвращает экземпляр объекта-коллекции AudioTrackList, представляющий набор звуковых дорожек, что хранятся в видеофайле.

var oATs = vidMain.audioTracks;


Объект AudioTrackList поддерживает свойство length, характерное для всех коллекций. Оно возвращает количество элементов в коллекции.

Каждый элемент этой коллекции представляет собой экземпляр объекта AudioTrack, представляющий одну из хранящихся в файле звуковых дорожек. Данный объект поддерживает следующие свойства:

  • language - возвращает обозначение языка данной звуковой дорожки в виде строки. Список всех обозначений поддерживаемых платформой Metro языков можно найти на[url=http://msdn.microsoft.com/en-us/library/ms533052(v=vs.85).aspx]этой Web-странице[/url]. Так, русский язык имеет обозначение ru, а американский английский - en-us.
  • label - возвращает описание звуковой дорожки в виде строки. Это описание может включать наименование языка, имена авторов голосового перевода и пр.
  • enabled - включает (значение true) или отключает (значение false) воспроизведение звуковой дорожки.


oATs[oATs.length - 1].enabled = true;


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

var oAT;
if (oATs.length > 1) {
  for (var i = 0; i < oATs.length; i++) {
    oAT = oATs[i];
    if (oAT.language == "ru") {
      oAT.enabled = true;
    } else {
      oAT.enabled = false;
    }
  }
}


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

4.2. Покадровое воспроизведение видео
Развитые приложения видеопроигрывателей предоставляют возможность покадрового воспроизведения видео. Это может пригодиться для исследования, скажем, записей, сделанных с камер видеонаблюдения.

Объект HTMLVideoObject поддерживает метод msFrameStep. В качестве единственного параметра он принимает логическое значение: true, если требуется выполнить переход на кадр вперёд, или false, если требуется выполнить переход на кадр назад. Результата он не возвращает.

vidMain.msFrameStep(true);



5. Оптимизация воспроизведения видео
В процессе воспроизведения видео платформа Metro максимально задействует аппаратные возможности устройства, в частности, схему декодирования, встроенную в графический контроллер, и цифровые сигнальные процессоры (DSP). В большинстве случаев эти возможности успешно задействуются, и видео воспроизводится плавно.

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

  • К видео была применена обводка (outline) CSS. (Параметры обводки задаются атрибутами стиля outline, outline-color, outline-style и outline-width.)
  • Видео выводится на канве (canvas) HTML.
  • Видео является частью векторного графического изображения SVG.


Надо сказать, что такие случаи встречаются очень редко, и, возможно, мы никогда с ними не столкнёмся. Но знать об этом всё же следует.


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

Запустим Visual Studio и создадим в нём новый проект. Дадим этому проекту имя VideoPlayer.

6.1. Интерфейс
Откроем файл default.html, в котором описывается интерфейс приложения, удалим содержимое парного тега <body> и впишем в него такой код:

<video id="vidMain" preload="auto"></video>

<div id="divTopAppBar" data-win-control="WinJS.UI.AppBar"
data-win-options="{layout: 'custom', placement: 'top'}">
  <div id="divProgress">
    <input type="range" id="sldProgress" min="0" step="1" value="0" disabled />
  </div>
  <div id="divTiming"></div>
</div>

<div id="divInfo" data-win-control="WinJS.UI.Flyout">
  <p>Ширина: <span id="spnWidth"></span></p>
  <p>Высота: <span id="spnHeight"></span></p>
</div>

<div id="divBottomAppBar" data-win-control="WinJS.UI.AppBar">
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnPlay', label: 'Пуск', icon: 'play', section: 'selection'}"></button>
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnPause', label: 'Пауза', icon: 'pause', section: 'selection'}"></button>
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnStop', label: 'Стоп', icon: 'stop', section: 'selection'}"></button>
  <hr data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'dvd1', section: 'selection', type: 'separator'}" />
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnClose', label: 'Закрыть', icon: 'clear', section: 'selection'}"></button>
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnOpen', label: 'Открыть', icon: 'openfile', section: 'global'}"></button>
  <hr data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{section: 'global', type: 'separator'}" />
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnMute', label: 'Тихо!', icon: 'mute', section: 'global', type: 'toggle'}"></button>
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnVolumeDown', label: 'Тише', icon: 'remove', section: 'global'}"></button>
  <button data-win-control="WinJS.UI.AppBarCommand"
  data-win-options="{id: 'btnVolumeUp', label: 'Громче', icon: 'add', section: 'global'}"></button>
</div>

t;></button>
</div>[/code]
Здесь мы, прежде всего, создали элемент видеопроигрывателя vidMain, в котором и будет воспроизводиться видео. Этот элемент будет выполнять предварительную загрузку заголовочной части видеофайла, что позволит нам сразу же получить сведения о нём.

Далее мы создали верхнюю панель инструментов divTopAppBar и превратили её в универсальную, задав для свойства layout значение custom. Из предыдущих статей этого цикла мы помним, что универсальная панель инструментов может включать в свой состав любые элементы управления: кнопки, флажки, переключатели, поляввода, списки и пр. (Обычные панели инструментов могут содержать только кнопки и разделители.)

Верхняя панель инструментов будет содержать два блока (блочных контейнера <div>). Левый блок займёт большую часть панели и включит в свой состав регулятор позиции воспроизведения sldProgress. Этот регулятор мы создали, указав для атрибута type формирующего его тега <input> значение range. Также мы указали для регулятора изначальную позицию, равную нулю (атрибут тега value), такую же минимальную позицию (атрибут тега min) и шаг, равный единице (атрибут тега step), а также сделали его изначально недоступным (атрибут тега без значения disabled). Правый же блок - divTiming - мы оставим пустым и будем впоследствии выводить в нём позицию воспроизведения файла (его хронометраж). Оба этих блока мы разместим в панели с применением сеточной разметки.

Ещё мы создали нижнюю панель инструментов divBottomAppBar, на этот раз обычную. Она включит в свой состав кнопки Пуск, Пауза, Стоп, Закрыть, Открыть, Тихо!, Тише и Громче, назначение которых понятно из надписей. Ещё в ней будут находиться два разделителя: первый разделит кнопки Стоп и Закрыть, второй - кнопки Открыть и Тихо!.

Отметим, что кнопка Тихо! является кнопкой-выключателем; при первом нажатии она будет отключать звук, при втором - включать. Мы знаем, что кнопки-выключатели создаются присвоением свойству type значения toggle. (У обычных кнопок это свойство имеет значение button.)

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

Наконец, мы создали всплывающий элемент, который будет выводить на экран сведения об открытом видеофайле. В этом элемента мы сформировали два абзаца, включающие встроенные контейнеры (теги <span>), в которых, собственно, и будут выводиться эти параметры. Отображаться на экране данный всплывающий элемент будет после нажатия на блок, выводящий хронометраж фильма.

6.2. Оформление
Откроем файл default.css, в котором описывается оформление интерфейса, и создадим в нём несколько стилей, описанных далее.

[code]#vidMain {
position: fixed;
width: 100%;
height: 100%;
}[/code]
Растягиваем видеопроигрыватель на весь экран, как было описано ранее.

[code]#divTopAppBar {
display: -ms-grid;
-ms-grid-columns: 1fr 300px;
-ms-grid-rows: 1fr;
}[/code]
Создаём сеточную разметку в верхней панели инструментов.

[code]#divProgress { -ms-grid-row-align: center; }[/code]
Для блока с регулятором позиции воспроизведения задаём вертикальное выравнивание по центру.

[code]#divTiming {
-ms-grid-column: 2;
font-size: 24pt;
text-align: right;
vertical-align: middle;
}[/code]
Задаём для блока, в котором будет выводиться хронометраж, размер шрифта в 24 пункта (атрибут стиля font-size), горизонтальное выравнивание по правому краю (атрибут стиля text-align) и вертикальное — по центру (атрибут стиля vertical-align).

[code]#sldProgress { width: 100%; }[/code]
И растягиваем регулятор позиции воспроизведения на всю ширину блока.

6.3. Логика
Переключимся на файл default.js, где пишется логика приложения.

Сначала введём код, объявляющий необходимые переменные.

[code]var btnPlay, btnPause, btnStop, btnOpen, btnClose, btnMute,
btnVolumeDown, btnVolumeUp, vidMain, sldProgress, divTiming,
ctrTopAppBar, ctrBottomAppBar, sCurrent, sDuration, ctrInfo,
spnWidth, spnHeight;
var d = new Date(0, 0, 0, 0, 0, 0, 0);[/code]
Напишем код, выполняющий инициализацию.

[code]document.addEventListener("DOMContentLoaded", function () {
WinJS.UI.processAll().then(function() {
ctrTopAppBar = document.getElementById("divTopAppBar").winControl;
ctrBottomAppBar = document.getElementById("divBottomAppBar").winControl;
btnPlay = ctrBottomAppBar.getCommandById("btnPlay");
btnPause = ctrBottomAppBar.getCommandById("btnPause");
btnStop = ctrBottomAppBar.getCommandById("btnStop");
btnOpen = ctrBottomAppBar.getCommandById("btnOpen");
btnClose = ctrBottomAppBar.getCommandById("btnClose");
btnMute = ctrBottomAppBar.getCommandById("btnMute");
btnVolumeDown = ctrBottomAppBar.getCommandById("btnVolumeDown");
btnVolumeUp = ctrBottomAppBar.getCommandById("btnVolumeUp");
vidMain = document.getElementById("vidMain");
sldProgress = document.getElementById("sldProgress");
divTiming = document.getElementById("divTiming");
ctrInfo = document.getElementById("divInfo").winControl;
spnWidth = document.getElementById("spnWidth");
spnHeight = document.getElementById("spnHeight");

btnPlay.addEventListener("click", btnPlayClick);
btnPause.addEventListener("click", btnPauseClick);
btnStop.addEventListener("click", btnStopClick);
btnOpen.addEventListener("click", btnOpenClick);
btnClose.addEventListener("click", btnCloseClick);
btnMute.addEventListener("click", btnMuteClick);
btnVolumeDown.addEventListener("click", btnVolumeClick);
btnVolumeUp.addEventListener("click", btnVolumeClick);
vidMain.addEventListener("click", vidMainClick);
vidMain.addEventListener("canplay", vidMainCanPlay);
vidMain.addEventListener("playing", vidMainPlaying);
vidMain.addEventListener("pause", vidMainPause);
vidMain.addEventListener("timeupdate", vidMainTimeUpdate);
sldProgress.addEventListener("change", sldProgressChange);
divTiming.addEventListener("click", divTimingClick);
ctrInfo.addEventListener("beforeshow", ctrInfoBeforeShow);

ctrBottomAppBar.hideCommands([btnPlay, btnPause, btnStop, "dvd1", btnClose]);
ctrTopAppBar.show();
ctrBottomAppBar.show();

vidMain.volume = 0.5;
});
});[/code]
Здесь мы получаем доступ ко всем необходимым элементам интерфейса и привязываем к ним обработчики событий. После этого мы скрываем всё содержимое левой секции нижней панели инструментов (о секциях панелей инструментов рассказывалось в предыдущих статьях цикла); в результате на экране останутся только те кнопки, что находятся в правой секций. Напоследок выводим обе панели инструментов на экран и задаём исходное значение громкости - половину от максимального.

Напишем функцию, которая будет выполняться в ответ на нажатие кнопки Открыть.

[code]function btnOpenClick() {
var oFP = new Windows.Storage.Pickers.FileOpenPicker();
oFP.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
oFP.suggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.videosLibrary;
oFP.fileTypeFilter.replaceAll([".avi", ".mp4", ".wmv"]);
oFP.pickSingleFileAsync().then(function (oFile) {
if (oFile) {
vidMain.src = URL.createObjectURL(oFile);
}
});
}[/code]
Она выведет на экран стандартный экран открытия файла, получит файл, указанный пользователем, и откроет его в видеопроигрывателе.

Здесь нужно отметить, что видеопроигрыватель поддерживает указание файлов исключительно в виде экземпляров объекта Blob. Получить этот экземпляр объекта мы можем вызовом метода createObjectURL объекта URL. (Подробнее обо всём этом говорилось в предыдущих статьях цикла.)

Объявим функцию-обработчик события canplay видеопроигрывателя.

[code]function vidMainCanPlay() {
ctrBottomAppBar.showCommands([btnPlay, btnStop, "dvd1", btnClose]);
sldProgress.value = 0;
sldProgress.max = vidMain.duration;
sldProgress.disabled = false;
d.setHours(0, 0, vidMain.duration);
sDuration = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
showTiming();
}[/code]
Сначала мы выводим на экран кнопки Пуск, Стоп и Закрыть и левый разделитель. Далее устанавливаем регулятор sldProgress в нулевое положение, указываем для него максимальное значение, равное продолжительности фильма, и делаем этот регулятор доступным. Теперь пользователь сможет запустить фильм на воспроизведение.

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

Сразу же объявим эту функцию.

[code]function showTiming() {
d.setHours(0, 0, vidMain.currentTime);
sCurrent = d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();
divTiming.textContent = sCurrent + " / " + sDuration;
}[/code]
Здесь нам всё уже знакомо.

Объявим функции-обработчики событий playing, pause и timeupdate видеопроигрывателя.

[code]function vidMainPlaying() {
ctrBottomAppBar.showCommands([btnPause]);
ctrBottomAppBar.hideCommands([btnPlay]);
ctrTopAppBar.hide();
ctrBottomAppBar.hide();
}
function vidMainPause() {
ctrBottomAppBar.showCommands([btnPlay]);
ctrBottomAppBar.hideCommands([btnPause]);
}
function vidMainTimeUpdate() {
sldProgress.value = vidMain.currentTime;
showTiming();
}[/code]
Первая функция делает кнопку Пуск недоступной, а кнопку Пауза — доступной, чтобы пользователь смог поставить воспроизведение на паузу; также она скрывает обе панели инструментов, чтобы не мозолили глаза пользователю. Вторая функция, наоборот, делает кнопку Пуск доступной, а кнопку Пауза — недоступной; таким образом, пользователь сможет возобновить воспроизведение фильма. Третья функция устанавливает регулятор sldProgress в положение, соответствующее текущей позиции воспроизведения фильма, и выводит на экран хронометраж.

И объявим функцию, которая станет обработчиком события click видеопроигрывателя.

[code]function vidMainClick() {
if ((ctrTopAppBar.hidden) || (ctrBottomAppBar.hidden)) {
ctrTopAppBar.show();
ctrBottomAppBar.show();
} else {
ctrTopAppBar.hide();
ctrBottomAppBar.hide();
}
}[/code]
Если хотя бы одна панель инструментов скрыта, обе они будут выведены на экран; в противном случае данная функция их скроет.

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

[code]function sldProgressChange() {
vidMain.currentTime = sldProgress.value;
}[/code]
Он просто устанавливает заданное в регуляторе значение в качестве текущей позиции воспроизведения фильма.

Объявим функцию, которая выполнится после щелчка на блоке, выводящем хронометраж.

[code]function divTimingClick() {
if (vidMain.videoWidth > 0) {
ctrInfo.show(divTiming, "bottom", "right");
}
}[/code]
Видеопроигрыватель предоставит нам значения ширины и высоты изображения у видеоролика только в том случае, если файл с этим роликом в нём уже открыт; в противном случае оба этих значения будут равны нулю. Так что сначала мы проверяем, доступны ли эти значения (не равно ли нулю значение ширины изображения), и, только удостоверившись в этом, выводим всплывающий элемент на экран ниже блока divTiming с выравниванием по правому краю.

Собственно получение и вывод размеров изображения выполнит обработчик события beforeshow всплывающего элемента.

[code]function ctrInfoBeforeShow() {
spnWidth.textContent = vidMain.videoWidth;
spnHeight.textContent = vidMain.videoHeight;
}[/code]
Значения соответствующих свойств видеопроигрывателя выводятся во встроенных контейнерах всплывающего элемента.

Настал черед остальных кнопок. Объявление функций, вызываемых при щелчках на кнопках Пуск и Пауза, будет таким:

[code]function btnPlayClick() {
vidMain.play();
}
function btnPauseClick() {
vidMain.pause();
}[/code]
Как говорится, комментарии излишни.

Напишем объявление функции, которая будет вызвана при нажатии кнопки Стоп:

[code]function btnStopClick() {
vidMain.pause();
sldProgress.value = 0;
vidMain.currentTime = 0;
showTiming();
}[/code]
Видеопроигрыватель HTML не предоставляет стандартной возможности полностью остановить воспроизведение фильма. Но для нас это не проблема. Мы поставим воспроизведение на паузу и установим позицию воспроизведения фильма в начало. Теперь пользователь сможет снова начать просмотр фильма.

На очереди — объявление функции, которая выполнится в ответ на нажатие кнопки Закрыть:

[code]function btnCloseClick() {
vidMain.src = "";
sldProgress.disabled = true;
ctrBottomAppBar.hideCommands([btnPlay, btnPause, btnStop, "dvd1", btnClose]);
divTiming.textContent = "";
}[/code]
Стандартного средства для закрытия загруженного файла видеопроигрыватель HTML также не предоставляет. Но где наша не пропадала! Мы присвоим свойству src видеопроигрывателя пустую строку, сделаем недоступным регулятор позиции воспроизведения, скроем кнопки Пуск, Пауза, Стоп и Закрыть вместе с левым разделителем и очистим блок, где выводится хронометраж фильма. Фактически мы вернём приложение в исходное состояние.

Код, объявляющий функцию, что будет выполнена при нажатии кнопки Тихо!, очень прост:

[code]function btnMuteClick() {
vidMain.muted = btnMute.selected;
}[/code]
Эта функция просто присваивает свойству muted видеопроигрывателя инвертированное значение свойства selected кнопки btnMute. (Свойство selected кнопки-выключателя хранит её состояние, то есть признак того, включена кнопка или нет.) Так что, если данная кнопка включена, звук будет приглушён, и наоборот.

Последнее, что мы сделаем, - объявим функцию, которая будет выполняться при щелчке на кнопках Тише и Громче.

[code]function btnVolumeClick (evt) {
var newVolume;
if (evt.target.winControl == btnVolumeDown) {
newVolume = vidMain.volume - 0.05;
if (newVolume < 0) {
newVolume = 0;
}
} else {
newVolume = vidMain.volume + 0.05;
if (newVolume > 1) {
newVolume = 1;
}
}
btnVolumeDown.disabled = (newVolume == 0);
btnVolumeUp.disabled = (newVolume == 1);
vidMain.volume = newVolume;
}[/code]
Мы просто уменьшаем или увеличиваем значение громкости и при достижении им пределов (0 и 1 соответственно) делаем соответствующие кнопки недоступными.

Всё! Сохраним все изменённые файлы и запустим приложение на выполнение. Интерфейс приложения должен выглядеть так, как показано на рис. 3.


Рис. 3. Интерфейс приложения видеопроигрывателя VideoPlayer


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


7. Заключение
Мы закончили рассмотрение основных средств платформы Metro для воспроизведения мультимедиа - звука и видео. А в качестве примера написали вполне функциональное приложение видеопроигрывателя.

Однако мультимедийные возможности Metro гораздо обширнее. Данная платформа предоставляет, в том числе, средства для перекодирования звука и видео (а также графики) в другие форматы, наложение на звук и видео различных эффектов, поддержку специализированных клавиш, предназначенных для управления воспроизведением, и др. Описание всего этого можно найти на Web-сайте MSDN, посвящённому Metro-программированию.


Дополнительные материалы



dronov_va, TheVista.Ru Team
Август 2012

  Передать на печать





Все права принадлежат © MSInsider.ru и TheVista.ru, 2013
Сайт является источником уникальной информации о семействе операционных систем Windows и других продуктах Microsoft. Перепечатка материалов возможна только с разрешения редакции.
Работает на WMS 1.1 (Страница создана за 0.104 секунд)