Перейти к содержанию

Вставка одного потока в другой

Описание

Начиная со сборки 5.3.37 в версии 5.3 улучшена функция вставки медиа в опубликованный поток. Добавлено новое REST API для вставки в опубликованный поток медиа или статической картинки из файла. Прежнее API для вставки потоков доступно в версии 5.2

Поддерживаемые кодеки

Видео:

  • H264

Аудио:

  • AAC (аудио файла-источника)
  • Opus (аудио целевого потока)
  • G711 (аудио целевого потока)

Картинка:

  • JPEG
  • PNG

Ограничения и требования к файлам

  1. Вставка не применяется к потокам звонков. Для звонков используются собственные технологии вставки аудио и видео.

  2. Последовательная вставка не поддерживается. Вставка предыдущего файла должна быть остановлена до вставки следующего.

  3. Если аудио кодек файла отличается от аудио кодека потока, на сервере будет работать транскодинг аудио. Это приводит к росту нагрузки на CPU.

  4. Видео дорожка в файле должна быть закодирована без использования B-фреймов, поскольку их проигрывание по WebRTC не поддерживается в браузерах.

  5. При кодировании файла MP4 для размещения на внешнем HTTPS сервере или S3 совместимом хранилище атом MOOV должен быть помещен в заголовок файла. В противном случае, необходимо будет выставить настройку vod_mp4_container_new=false и ждать полного скачивания файла для вставки в поток.

REST API для вставки

REST-запрос должен быть HTTP/HTTPS POST запросом в таком виде:

  • HTTP: http://test.flashphoner.com:8081/rest-api/stream/inject3/startup
  • HTTPS: https://test.flashphoner.com:8444/rest-api/stream/inject3/startup

Здесь:

  • test.flashphoner.com - адрес WCS-сервера
  • 8081 - стандартный REST / HTTP порт WCS-сервера
  • 8444 - стандартный HTTPS порт
  • rest-api - обязательная часть URL
  • /stream/inject3/startup - используемый REST-метод

REST методы и ответы

/stream/inject3/startup

Вставить медиа дорожку или статическую картинку в поток. Начиная со сборки 5.3.44, также можно указать fps и gop при вставке картинки

Request example
POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/video.mp4",
            "media": "video"
        },
        {
            "uri": "file:///opt/media/audio.mp4",
            "media": "audio"
        }
    ],
    "audio": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "video": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "loop": true,
    "speed": 1,
    "duration": 20
}
Response example
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Return codes
Code Reason
200 OK
400 Bad request
404 Not found
409 Conflict
500 Internal error

/stream/inject3/update

Начиная со сборки 5.3.44 можно обновить характеристики текущей вставки

  • При обновлении duration длительность рассчитывается относительно времени вызова stream/inject3/startup
  • Такие параметры как resources, audio и video не доступны для обновления после вставки в поток
Request example
POST /rest-api/stream/inject3/find_all HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "fps": 1,
    "gop": 100,
    "speed": 0.5,
    "duration": 100,
    "loop": false
}
Response example
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Return codes
Code Reason
200 OK
400 Bad request
404 Not found

/stream/inject3/find_all

Найти все активные вставки на сервере. Ответы будут отличаться в зависимости от сборки

Request example
POST /rest-api/stream/inject3/find_all HTTP/1.1
Host: localhost:8081
Content-Type: application/json
Response example

До сборки 5.3.44

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json

[
    {
        "streamName": "test",
        "videoInjectorInfo": {
            "resource": {
                "uri": "file:///opt/media/video.mp4",
                "media": "video"
            },
            "startTime": 1749106490411
        },
        "audioInjectorInfo": {
            "resource": {
                "uri": "file:///opt/media/audio.mp4",
                "media": "audio"
            },
            "startTime": 1749106490410
        }
    }
]

Начиная со сборки 5.3.44

  • timer указывается в теле ответа только в случае, если был установлен duration через stream/inject3/startup или stream/inject3/update

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: *
    Content-Type: application/json
    
    [
        {
            "streamName": "test",
            "videoInjectorInfo": {
                "resource": {
                    "uri": "file:///opt/media/img.png",
                    "media": "image"
                },
                "startTime": 1749106490411,
                "fps": 1,
                "gop": 1
            },
            "audioInjectorInfo": {
                "resource": {
                    "uri": "file:///opt/media/audio.mp4",
                    "media": "audio"
                },
                "startTime": 1749106490410,
                "speed": 1.0
            },
            "loop": true,
            "timer" : {
                "duration": 60,
                "left": 50
            }
        }
    ]
    

  • Нижеуказанные в теле ответа fps и gop являются характеристиками, которые зашиты в mp4-файл.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json

[
    {
        "streamName": "test",
        "videoInjectorInfo": {
            "resource": {
                "uri": "file:///opt/media/audio_video.mp4",
                "media": "audio,video"
            },
            "startTime": 1753067834355,
            "speed": 1.0,
            "fps": 25,
            "gop": 75
        },
        "audioInjectorInfo": {
            "resource": {
                "uri": "file:///opt/media/audio_video.mp4",
                "media": "audio,video"
            },
            "startTime": 1753067834355,
            "speed": 1.0
        },
        "loop": true,
    }
]
Return codes
Code Reason
200 OK
404 Not found

stream/inject3/terminate

Остановить вставку в поток. Запросы будут отличаться в зависимости от сборки

Request example

До сборки 5.3.44 существует возможность прекратить вставку отдельной дорожки, при этом начнется воспроизведение дорожки оригинального стрима

POST /rest-api/stream/inject3/terminate HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "video": true,
    "audio": false
}

Начиная со сборки 5.3.44 запрос полностью прерывает вставку

POST /rest-api/stream/inject3/terminate HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test"
}
Response example
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Return codes
Code Reason
200 OK
400 Bad request
404 Not found
500 Internal error

Параметры

Parameter Description Example
localStreamName Имя потока, в который производится вставка test
resources Список медиа источников для вставки [{"uri": "file:///opt/media/video.mp4", "media": "video"}]
-- uri URI медиа источника file:///opt/media/video.mp4
-- media Тип медиа источника video
audio Условия для вставки аудио {"required": true, "wait": true, "stub": true}
video Условия для вставки видео {"required": true, "wait": true, "stub": true}
-- required Источник обязателен для вставки true
-- wait Ждать готовности другого источника (Аудио ждет видео и наоборот) true
-- stub Отображать черный квадрат или тишину до готовности источника true
loop Зациклить вставку false
duration Длительность вставки в секундах 20
speed Скорость проигрывания вставляемого медиа [0.1, 4]
fps Количество кадров в секунду (Влияет только на вставку картинки), доступно со сборки 5.3.44 [1, 100]
gop Количество фреймов в группе (Влияет только на вставку картинки), доступно со сборки 5.3.44 [1, 1000]
timer Возвращается в ответ на запрос inject3/find_all, если было указан duration {"timer": {"duration":60, "left":54} }
-- duration Длительность вставки 60
-- left Оставшееся время вставки с момента вызова inject3/startup 54

Способы применения вставки

Вставка аудио и видео

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

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/video.mp4",
            "media": "audio, video"
        }
    ],
    "audio": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "video": {
        "required": true,
        "wait": true,
        "stub": true
    }
}

или из двух различных источников

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/audio.mp4",
            "media": "audio"
        },
        {
            "uri": "file:///opt/media/video.mp4",
            "media": "video"
        }
    ],
    "audio": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "video": {
        "required": true,
        "wait": true,
        "stub": true
    }
}

Если один из источников не готов к вставке (например, файл не полностью загружен) и параметр wait для другого источника установлен в true, вставка не начнется до готовности источника (полного скачивания файла и его разбора).

Если установлен параметр duration, вставка остановится по истечении заданного времени длительности. Если этот параметр не установлен, вставка остановится по окончании хотя бы одного из файлов. В любом случае, вставка остановится по запросу /stream/inject3/terminate

POST /rest-api/stream/inject3/terminate HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test"
}

Вставка только видео или только аудио

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

В этом примере, вставляется только видео

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/video.mp4",
            "media": "video"
        }
    ],
    "audio": {
        "required": false,
        "wait": false,
        "stub": false
    },
    "video": {
        "required": true,
        "wait": false,
        "stub": false
    }
}

А в этом примере заменяется только аудио

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/audio.mp4",
            "media": "audio"
        }
    ],
    "audio": {
        "required": true,
        "wait": false,
        "stub": false
    },
    "video": {
        "required": false,
        "wait": false,
        "stub": false
    }
}

Вставка статической картинки

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

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/audio.mp4",
            "media": "audio"
        },
        {
            "uri": "file:///opt/media/image.png",
            "media": "image"
        }
    ],
    "audio": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "video": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "loop": true,
    "duration": 10
}

Это полезно, например, в качестве заставки для зрителей, ожидающих начала трансляции.

Начиная со сборки 5.3.44 можно указать fps и gop для вставки картинки

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/audio.mp4",
            "media": "audio"
        },
        {
            "uri": "file:///opt/media/image.png",
            "media": "image"
        }
    ],
    "audio": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "video": {
        "required": true,
        "wait": true,
        "stub": true
    },
    "loop": true,
    "duration": 10,
    "fps": 1,
    "gop": 10
}

Вставка с зацикливанием

При завершении считывания из файла, повторная подготовка ресурса к вставке происходит согласно установленным настройкам vod_mp4_container_new и кэширования

В случае вставки аудио и видео из разных файлов

POST /rest-api/stream/inject3/startup HTTP/1.1
Host: localhost:8081
Content-Type: application/json

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/short_audio.mp4",
            "media": "audio"
        },
        {
            "uri": "file:///opt/media/long_video.mp4",
            "media": "video"
        }
    ],
    "audio": {
        "required": true,
        "wait": false,
        "stub": false
    },
    "video": {
        "required": true,
        "wait": false,
        "stub": false
    },
    "loop": true
}

До сборки 5.3.44 зацикливание отдельных файлов независимо друг от друга

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

Возможные варианты размещения файла для вставки

Файл для вставки может находиться на том же сервере, локально

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "file:///opt/media/video.mp4",
            "media": "audio, video"
        }
    ]
}

Файл также может быть загружен по HTTP/HTTPS с внешнего сервера или из CDN

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "https://cdn/downloads/video.mp4",
            "media": "audio, video"
        }
    ]
}

либо из S3 совместимого хранилища

{
    "localStreamName": "test",
    "resources": [
        {
            "uri": "s3/bucket/video.mp4",
            "media": "audio, video"
        }

    ]
}

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

Настройка WCS

Новая реализация транскодинга должна быть отключена до сборки 5.3.42

transcoding_gen3=false

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

periodic_fir_request=true

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

vod_mp4_container_new=true

Настройка доступа к S3 хранилищу

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

AWS

Параметры для доступа к AWS S3 должны быть указаны в файле flashphoner.properties следующим образом

aws_s3_credentials=zone;login;hash

Здесь

  • zone - AWS регион, где располагается хранилище
  • login - Access Key ID
  • hash - Secret Accesss Key

Пример настройки доступов к AWS S3:

aws_s3_credentials=eu-central-1;AA22BB33CC44DE;DhlAkpZ4adclHhbLwhTNL4hvWTo80Njo

Digital Ocean Spaces

Параметры для доступа к DO Spaces должны быть указаны следующим образом

aws_s3_credentials=ams3;access_key;secret

Здесь

  • ams3 - поддомен digitaloceanspaces.com (Амстердам в данном примере)
  • access_key - storage access key
  • secret - storage access secret code

Selectel

параметры для доступа к Selectel S3 должны быть указаны следующим образом

aws_s3_credentials=ru-1a;login;password

Здесь

  • ru-1a - регион, гда располагается хранилище
  • login - имя пользователя
  • password - пароль

Настройка кэширования файлов

Начиная со сборки 5.3.38 файлы с внешних хранилищ могут кэшироваться для снижения входящего на сервер трафика и времени ожидания

http_client_cache_enabled=true
http_client_cache_path=.cache-http
http_client_max_cache_size=1g

В этом случае, файловый кэш будет размещен в каталоге /usr/local/FlashphonerWebCallServer/.cache-http (если такого каталога нет, WCS попытается его создать). Если суммарный размер всех загруженных файлов превысит 1 Гб (в данном примере), наиболее старые файлы будут удалены для освобождения места. Размер проверяется после завершения скачивания файла, поэтому в моменте каталог кэша может занимать на диске больше места, чем заданный максимальный размер.

Каталог для кэша должен быть доступен для записи пользователю flashphoner

drwxrwxr-x  2 flashphoner flashphoner 4096 Jun 19 00:28 .cache-http

Сожержимое заголовка Cache-Control может быть указано явно при помощи параметра

http_client_cache_control=max-age=0

Это может быть полезно для управления поведением внешнего сервера при скачивании файла.