Flash Media Server: восстановление просмотра с точки останова

This post is also available in: Английский


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

Эта задача была бы очень простой, если бы воспроизведение всегда прекращалось по нажатию кнопки «стоп» в плеере. Но в реальности выделенную кнопку останова в плеере обычно не делают, а к прерыванию просмотра может приводить целый ряд причин:

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

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

События DOM onbeforeunload или onunload

События onbeforeunload и onunload возникают для объекта window соответственно до события onunload и, когда пользователь уходит со страницы. Пример обработки:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<HTML>
<head>
<script>
function closeIt()
{
  return "Any string value here forces a dialog box to \n" +
         "appear before closing the window.";
}
window.onbeforeunload = closeIt;
</script>
</head>
<body>
</body>
</html>

Перед закрытием страницы мы имеем возможность в последний момент отправить текущую позицию видео на сервер.

Недостатки: поддерживается не всеми браузерами (например, Opera), необходимо использовать синхронный AJAX-запрос, работает не во всех случаях, перечисленных выше.

Периодическая отправка на сервер текущей позиции

Через определенные интервалы времени в ходе воспроизведения видео мы можем отправлять на сервер текущую позицию. Пример на ActionScript:

1
2
3
4
5
            setTimeout(function () {
                if (video.playing) {
                    sendPosition(video.position);
                }
            }, 5000);

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

Недостатки: приходится искать компромисс между точностью запомненной позиции и снижением нагрузки на сервер по числу запросов.

Анализ лог-файлов Flash Media Server-а

Adobe FMS ведет подробный access.log для всех событий воспроизведения видео, включая событие прекращения воспроизведения в независимости от причины, по которой оно произошло. Пример записи в access.log:

1
2
3
4
5
6
#Version: 1.0
#Start-Date: 2011-01-17 00:01:13
#Software: Adobe Flash Media Server 4.0.0 r1121 x64
#Date: 2011-01-17
#Fields: x-category     x-event date    time    x-pid   c-ip    c-client-id     cs-bytes        c-referrer       sc-bytes        x-sname x-spos  sc-stream-bytes x-file-size     x-file-length   x-trans-sname   x-status       x-comment
stream  stop    2011-01-17      11:03:57        16877   94.25.145.221   4702111234508538223     3567    http://www.example.com/player_test/fl_player.swf?sid=4r01p81fvvooj20bumhggevu12&uniqid=4ac11015f655b9335fe246a8ed24ae55&uid=136      1378700 8ca9dc3d48cab6e5f8a8d42844c5c91d        5990    1210365 343845829       5111.269043     -       210     -

При анализе лог-файла для целей данной статьи в первую очередь следует обратить внимание на события stream pause и stream stop, в которых содержится точка останова. А события connection connect и disconnect помогут отделить сессии просмотра, которые уже завершились, от тех, которые еще продолжаются в момент обработки лога. Группировать строки лог-файла, относящиеся к одной сессии просмотра можно по различным полям, главное, чтобы их сочетание было уникальным для каждой сессии. В FMS есть для этого специальное поле c-client-id, но полагаться только на него не стоит, так как его значение может повторяться для различных сессий. Чтобы получить по-настоящему уникальный идентификатор, вы можете сгенерировать его на стороне клиента и добавить к URL вызова плеера.

В логе помимо прочей полезной информации присутствует тип события, позиция воспроизведения, на которой возникло событие, имя файла контента. Любую дополнительную информацию, которой FMS сам по себе не располагает, можно передать в лог через поле c-referrer. В это поле записывается URL, который был использован для загрузки плеера, воспроизводящего контент. Такую информацию, как идентификатор пользователя, идентификатор сессии, различные признаки платного контента (цена, тариф и т.д.) вы можете закодировать в строке URL плеера в любом удобном для обработки виде. Наиболее простой и очевидный вариант — в формате строки параметров GET-запроса.

Достоинства: возможность получения точной позиции при любых причинах останова.

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

Использование Web-сокетов

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

Пример работы с web-сокетами с использованием библиотеки Socket.io. Клиентская часть:

1
2
3
4
var socket = new io.Socket();
function playerSendEventCallback(position) {
socket.send(position);
}

Серверная часть:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var http = require('http'),  
io = require('socket.io'),
server = http.createServer(function(req, res){
 res.writeHeader(200, {'Content-Type': 'text/html'});
 res.writeBody('Hello world');
 res.finish();
});
server.listen(80);
var socket = io.listen(server);
socket.on('connection', function(client){
  client.on('message', function(message){
     savePosition(message);
  });
  client.on('disconnect', function(){ … });
});
function savePosition(position) { ... }

Недостатки: технология web-сокетов относительно новая и пока еще не поддерживается всеми распространенными версиями браузеров.

Заключение

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

ZSP4MAR7EA4N