Tech

Injecting

Injecting puts a file in place of a live stream. The publisher keeps publishing, viewers keep playing the same stream, and the Media Server sends them the file instead: a video, a sound or a picture.

Here too the Media Server is the one who starts. You tell it over the REST API which stream and which file, and the server injects the file until it ends, until the time you set runs out, or until you tell it to stop. Then viewers get the stream again.

This is how a broadcast shows an intro before it starts, a break screen or an ad, with no changes on the publisher’s side and no reconnection on the viewers’.

Injecting a file

One REST API call, /inject3/startup, gives the Media Server the name of a published stream and the file to inject into it, such as file:///opt/media/break.mp4. The server reads the file and sends it to the stream’s viewers in place of what the publisher sends.

The viewers don’t reconnect and don’t notice the switch. They play the same stream under the same name, over WebRTC, HLS or any other protocol, and the stream can be recorded, mixed or pushed elsewhere with the file in it.

The call /inject3/terminate stops the injection, and viewers get the publisher’s stream again. Video in the file goes as H.264 or H.265, audio as AAC; the server turns the AAC into the stream’s Opus or G.711, which takes some CPU.

From your backend, with a small helper for the REST API:

Inject and go back

Inject a file into the stream live, and give the stream back to the publisher:

const api = 'https://media.example.com:8444/rest-api/v3';
const post = (method, body) =>
  fetch(`${api}/inject3/${method}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });

await post('startup', {
  localStreamName: 'live',
  resources: [
    {
      uri: 'file:///opt/media/break.mp4',
      media: 'audio,video',
    },
  ],
});

// back to the live stream
await post('terminate', { localStreamName: 'live' });
Explore REST API↗

For a while

Inject the file for a minute, over and over, and go back by itself:

await post('startup', {
  localStreamName: 'live',
  resources: [
    {
      uri: 'file:///opt/media/ad.mp4',
      media: 'audio,video',
    },
  ],
  loop: true,
  duration: 60,
});

Without duration the injection stops when the file ends, or never, with loop. One injection at a time: stop the one before to start another.

Explore REST API↗

Explore documentation↗

Video and audio slots

A stream has two slots, video and audio, and every file in resources takes one or both: audio,video, video or audio. Video and audio can come from one file or from two.

The server goes down the list in its order. A slot takes the first file that is there and has its track; a file that can’t be found or read is passed over for the next one, and once both slots are taken, the rest of the list is skipped. So a list can carry a spare: a file on a CDN first, its copy on the server next.

With the post helper from above:

Two files

The picture from one file, the sound from another, started together:

await post('startup', {
  localStreamName: 'live',
  resources: [
    { uri: 'file:///opt/media/video.mp4', media: 'video' },
    { uri: 'file:///opt/media/audio.mp4', media: 'audio' },
  ],
  video: { required: true, wait: true, stub: true },
  audio: { required: true, wait: true, stub: true },
});

Video only

The file’s picture with the presenter’s voice:

await post('startup', {
  localStreamName: 'live',
  resources: [
    { uri: 'file:///opt/media/slides.mp4', media: 'video' },
  ],
  video: { required: true, wait: false, stub: false },
  audio: { required: false, wait: false, stub: false },
});

Audio only

The presenter’s picture with the file’s sound:

await post('startup', {
  localStreamName: 'live',
  resources: [
    { uri: 'file:///opt/media/music.mp4', media: 'audio' },
  ],
  video: { required: false, wait: false, stub: false },
  audio: { required: true, wait: false, stub: false },
});

Looped together

A short sound under a long video, both started over by the shorter one:

await post('startup', {
  localStreamName: 'live',
  resources: [
    {
      uri: 'file:///opt/media/long_video.mp4',
      media: 'video',
    },
    {
      uri: 'file:///opt/media/short_audio.mp4',
      media: 'audio',
    },
  ],
  loop: true,
});

While the files are getting ready for the next round, viewers get a black frame and silence.

Explore documentation↗

Still image

A picture, PNG or JPEG, goes into the video slot as image, alone or with a sound. The server makes a video of it at the frame rate set in fps, with a key frame every gop frames.

A picture with music is what viewers see before the broadcast starts or while the presenter is away: inject it in a loop when the stream goes on the air, and stop it when the presenter is ready.

A picture with music

One frame a second, a key frame every ten, for ten minutes at most:

await post('startup', {
  localStreamName: 'live',
  resources: [
    { uri: 'file:///opt/media/waiting.png', media: 'image' },
    { uri: 'file:///opt/media/music.mp4', media: 'audio' },
  ],
  loop: true,
  duration: 600,
  fps: 1,
  gop: 10,
});
Explore REST API↗

Explore documentation↗

Injecting controls

A running injection can be changed with /inject3/update: its speed, from 0.1 to 4 times, its duration, counted from the start, the loop, and the frame rate and key frames of a picture. The files and the slots stay as they were started.

Change it

Play the file at half speed, stop looping and end it 100 seconds after the start:

await post('update', {
  localStreamName: 'live',
  speed: 0.5,
  loop: false,
  duration: 100,
});
Explore REST API↗

Launch your streaming server

LLcast will deploy a dedicated Media Server just for you. Publish your stream once, and put an intro, a break screen or an ad into it whenever you need, over the REST API, from files on the server, on a CDN or in S3.