WCS предоставляет возможность захвата медиапотока из файла MP4, расположенного на локальном диске сервера (Video on Demand, VOD). Полученный поток можно воспроизвести, ретранслировать, управлять им, как любым потоком на WCS-сервере. Прежде всего, данная возможность предназначена для воспроизведения записанных ранее трансляций в браузере или мобильном приложении клиента.
Для захвата VOD из файла в качестве имени потока при вызове функции session.createStream() должна быть указана ссылка на файл в виде:
vod://sample.mp4 |
где sample.mp4 - имя файла, который должен находиться в каталоге /usr/local/FlashphonerWebCallServer/media/
В случае, если файл с таким именем отсутствует, сервер вернет сообщение StreamStatusEvent FAILED, в поле "info" которого будет указан диагноз "File not found".
Поток, созданный таким образом, предназначен для трансляции одному пользователю (персональный VOD). В случае, если необходимо организовать полноценную онлайн-трансляцию, следует указать ссылку на файл в виде:
vod-live://sample.mp4 |
К такому потоку могут подключиться одновременно несколько пользователей в реальном времени.
1. Для теста используем веб-приложение Player для воспроизведения файла.
2. Загрузите файл в каталог /usr/local/FlashphonerWebCallServer/media/
3. Откройте веб-приложение Player, укажите в поле Stream имя файла:
4. Нажмите Start. Начнется воспроизведение файла:
5. Нажмите Stop для остановки воспроизведения.
6. Удалите файл из каталог /usr/local/FlashphonerWebCallServer/media/
7. Нажмите Start. Отобразится статус FAILED и сообщение "File not found":
Ниже описана последовательность вызовов при использовании:
примера Stream Recording для публикации потока и записи файла
примера Player для воспроизведения VOD-потока
1. Установка соединения с сервером для публикации и записи потока.
Flashphoner.createSession(); code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){ ... }); |
2. Получение от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){ setStatus(session.status()); //session connected, start playback publishStream(session); }).on(SESSION_STATUS.DISCONNECTED, function(){ ... }).on(SESSION_STATUS.FAILED, function(){ ... }); |
3. Публикация потока с указанием признака записи:
stream.publish(); code
session.createStream({ name: streamName, display: localVideo, record: true, receiveVideo: false, receiveAudio: false ... }).publish(); |
4. Получение от сервера события, подтверждающего успешную публикацию потока.
StreamStatusEvent, статус PUBLISHING code
session.createStream({ name: streamName, display: localVideo, record: true, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.PUBLISHING, function(stream) { setStatus(stream.status()); onStarted(stream); }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { ... }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); |
5. Отправка аудио-видео потока по WebRTC
6. Остановка публикации потока.
stream.stop(); code
function onStarted(stream) { $("#publishBtn").text("Stop").off('click').click(function(){ $(this).prop('disabled', true); stream.stop(); }).prop('disabled', false); } |
7. Получение от сервера события, подтверждающего остановку публикации потока.
StreamStatusEvent, статус UNPUBLISHED code
session.createStream({ name: streamName, display: localVideo, record: true, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.PUBLISHING, function(stream) { ... }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { setStatus(stream.status()); showDownloadLink(stream.getRecordInfo()); onStopped(); }).on(STREAM_STATUS.FAILED, function(stream) { ... }).publish(); |
8. Установка соединения с сервером для воспроизведения потока.
Flashphoner.createSession(); code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){ ... }); |
9. Получение от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED code
Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function(session){ setStatus(session.status()); //session connected, start playback playStream(session); }).on(SESSION_STATUS.DISCONNECTED, function(){ ... }).on(SESSION_STATUS.FAILED, function(){ ... }); |
10. Воспроизведение потока.
stream.play(); code
if (Flashphoner.getMediaProviders()[0] === "MSE" && mseCutByIFrameOnly) { options.mediaConnectionConstraints = { cutByIFrameOnly: mseCutByIFrameOnly } } if (resolution_for_wsplayer) { options.playWidth = resolution_for_wsplayer.playWidth; options.playHeight = resolution_for_wsplayer.playHeight; } else if (resolution) { options.playWidth = resolution.split("x")[0]; options.playHeight = resolution.split("x")[1]; } stream = session.createStream(options).on(STREAM_STATUS.PENDING, function(stream) { ... }); stream.play(); |
11. Получение от сервера события, подтверждающего успешное воспроизведение потока.
StreamStatusEvent, статус PLAYING code
stream = session.createStream(options).on(STREAM_STATUS.PENDING, function(stream) { ... }).on(STREAM_STATUS.PLAYING, function(stream) { $("#preloader").show(); setStatus(stream.status()); onStarted(stream); }).on(STREAM_STATUS.STOPPED, function() { ... }).on(STREAM_STATUS.FAILED, function(stream) { ... }).on(STREAM_STATUS.NOT_ENOUGH_BANDWIDTH, function(stream){ ... }); stream.play(); |
12. Прием аудио-видео потока по Websocket и воспроизведение по WebRTC
13. Остановка воспроизведения потока.
stream.stop(); code
function onStarted(stream) { $("#playBtn").text("Stop").off('click').click(function(){ $(this).prop('disabled', true); stream.stop(); }).prop('disabled', false); ... } |
14. Получение от сервера события, подтверждающего остановку воспроизведения потока.
StreamStatusEvent, статус STOPPED code
stream = session.createStream(options).on(STREAM_STATUS.PENDING, function(stream) { ... }).on(STREAM_STATUS.PLAYING, function(stream) { ... }).on(STREAM_STATUS.STOPPED, function() { setStatus(STREAM_STATUS.STOPPED); onStopped(); }).on(STREAM_STATUS.FAILED, function(stream) { ... }).on(STREAM_STATUS.NOT_ENOUGH_BANDWIDTH, function(stream){ ... }); stream.play(); |
Для трансляций vod-live поддерживается циклический захват потока, после окончания файла захват начинается сначала. Эта возможность включается настройкой в файле flashphoner.properties
vod_live_loop=true |
Поток может быть захвачен из файла, размещенного на AWS в хранилище S3. В отличие от VOD захвата файла с локального диска, файл, размещенный на внешнем хранилище, загружается и воспроизводится последовательно.
Для захвата VOD из файла на AWS в качестве имени потока при вызове функции session.createStream() должна быть указана ссылка на файл в виде:
vod://s3/bucket/sample.mp4 |
где
1. Браузер запрашивает захват потока из файла на AWS
2. WCS сервер направляет запрос AWS
3. Файл загружается на WCS сервер
4. WebRTC поток из файла передается в браузер для воспроизведения
Для загрузки файлов из AWS необходимо указать в файле настроек flashphoner.properties данные для доступа к хранилищу S3
aws_s3_credentials=zone;login;hash |
Чтобы захватывать поток из файла во время его загрузки, необходимо указать следующую настройку
vod_mp4_container_new=true |
Заголовок (moov) должен всегда располагаться перед данными (mdat). Примерная структура файла должна быть такой:
Atom ftyp @ 0 of size: 32, ends @ 32 Atom moov @ 32 of size: 357961, ends @ 357993 Atom mvhd @ 40 of size: 108, ends @ 148 Atom trak @ 148 of size: 127708, ends @ 127856 Atom tkhd @ 156 of size: 92, ends @ 248 Atom edts @ 248 of size: 48, ends @ 296 Atom elst @ 256 of size: 40, ends @ 296 Atom mdia @ 296 of size: 127560, ends @ 127856 Atom mdhd @ 304 of size: 32, ends @ 336 Atom hdlr @ 336 of size: 45, ends @ 381 Atom minf @ 381 of size: 127475, ends @ 127856 Atom vmhd @ 389 of size: 20, ends @ 409 Atom dinf @ 409 of size: 36, ends @ 445 Atom dref @ 417 of size: 28, ends @ 445 Atom url @ 433 of size: 12, ends @ 445 Atom stbl @ 445 of size: 127411, ends @ 127856 Atom stsd @ 453 of size: 171, ends @ 624 Atom avc1 @ 469 of size: 155, ends @ 624 Atom avcC @ 555 of size: 53, ends @ 608 Atom pasp @ 608 of size: 16, ends @ 624 ~ Atom stts @ 624 of size: 24, ends @ 648 Atom stss @ 648 of size: 568, ends @ 1216 Atom stsc @ 1216 of size: 28, ends @ 1244 Atom stsz @ 1244 of size: 63308, ends @ 64552 Atom stco @ 64552 of size: 63304, ends @ 127856 Atom trak @ 127856 of size: 230039, ends @ 357895 Atom tkhd @ 127864 of size: 92, ends @ 127956 Atom edts @ 127956 of size: 36, ends @ 127992 Atom elst @ 127964 of size: 28, ends @ 127992 Atom mdia @ 127992 of size: 229903, ends @ 357895 Atom mdhd @ 128000 of size: 32, ends @ 128032 Atom hdlr @ 128032 of size: 45, ends @ 128077 Atom minf @ 128077 of size: 229818, ends @ 357895 Atom smhd @ 128085 of size: 16, ends @ 128101 Atom dinf @ 128101 of size: 36, ends @ 128137 Atom dref @ 128109 of size: 28, ends @ 128137 Atom url @ 128125 of size: 12, ends @ 128137 Atom stbl @ 128137 of size: 229758, ends @ 357895 Atom stsd @ 128145 of size: 106, ends @ 128251 Atom mp4a @ 128161 of size: 90, ends @ 128251 Atom esds @ 128197 of size: 54, ends @ 128251 Atom stts @ 128251 of size: 32, ends @ 128283 Atom stsc @ 128283 of size: 47500, ends @ 175783 Atom stsz @ 175783 of size: 118804, ends @ 294587 Atom stco @ 294587 of size: 63308, ends @ 357895 Atom udta @ 357895 of size: 98, ends @ 357993 Atom meta @ 357903 of size: 90, ends @ 357993 Atom hdlr @ 357915 of size: 33, ends @ 357948 Atom ilst @ 357948 of size: 45, ends @ 357993 Atom ©too @ 357956 of size: 37, ends @ 357993 Atom data @ 357964 of size: 29, ends @ 357993 Atom free @ 357993 of size: 8, ends @ 358001 Atom mdat @ 358001 of size: 212741950, ends @ 213099951 |
REST-запрос должен быть HTTP/HTTPS POST запросом в таком виде:
Здесь:
REST-метод | Пример тела REST-запроса | Пример тела REST-ответа | Статусы ответа | Описание | ||
---|---|---|---|---|---|---|
/vod/startup |
| 409 - Conflict 500 - Internal error | Захватить поток из указанного файла | |||
/vod/find |
|
| 200 – потоки найдены 404 – потоки не найдены | Найти VOD-потоки по указанному критерию | ||
/vod/find_all |
| 200 – потоки найдены 404 – потоки не найдены | Найти все VOD-потоки | |||
/vod/terminate |
| 200 - поток завершен 404 - поток не найден | Завершить VOD-поток |
Имя параметра | Описание | Пример |
---|---|---|
uri | Имя файла для захвата потока | vod://sample.mp4 |
localStreamName | Имя создаваемого потока | test |
status | Текущий статус потока | PROCESSED_LOCAL |
localMediaSessionId | Идентификатор медиасессии | 29ec3236-1093-42bb-88d6-d4ac37af3ac0 |
hasAudio | В потоке есть аудио | true |
hasVideo | В потоке есть видео | true |
record | Поток записывается | false |
1) AAC фреймы типа 0 не поддерживаются декодером и будут игнорироваться при воспроизведении захваченного потока
При этом в клиентском логе будут выведены предупреждения:
10:13:06,815 WARN AAC - AudioProcessor-c6c22de8-a129-43b2-bf67-1f433a814ba9 Dropping AAC frame that starts with 0, 119056e500 |