Skip to content

Commit

Permalink
More configuration options for the pipeline.
Browse files Browse the repository at this point in the history
This allows the user to customize the availability window and the
presentation delay for live packaging, as well as a time range
extraction option for VOD. The presentation delay will only be
set in the DASH manifest because there is currently no equivalent
option for HLS.

Closes #1
Closes #2
Closes #3

Change-Id: I20041c4697b675b62a6ec170fe3f9687abe3931a
  • Loading branch information
vickywmin committed Sep 5, 2019
1 parent 1ee56cc commit 83d26db
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 20 deletions.
8 changes: 7 additions & 1 deletion config_files/input_vod_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@ inputs:
resolution: 4k
# Whether or not the video frames are interlaced.
is_interlaced: False
# Start time of video to encode.
start_time: 00:00:00
# End time of video to encode.
end_time: 00:20:00

# A second track (audio) from the same input file.
- name: Sintel.2010.4k.mkv
track_num: 1
media_type: audio
start_time: 00:00:00
end_time: 00:20:00

# Several text tracks of different languages.
# https://storage.googleapis.com/shaka-streamer-assets/sample-inputs/Sintel.2010.Arabic.vtt
Expand Down Expand Up @@ -64,4 +70,4 @@ inputs:
# https://storage.googleapis.com/shaka-streamer-assets/sample-inputs/Sintel.2010.Chinese.vtt
- name: Sintel.2010.Chinese.vtt
media_type: text
language: zh
language: zh
4 changes: 4 additions & 0 deletions config_files/pipeline_live_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ packager:
- hls
# Length of each segment in seconds.
segment_size: 4
# Availability window for live packaging.
availability_window: 400
# Set the presentation delay for live packaging.
presentation_delay: 50
8 changes: 8 additions & 0 deletions streamer/default_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
# TODO: Add different default config input entries for audio, video and
# text entries so that fields match up with each type of entry.
'language': 'und',
# Start time of VOD input to encode.
'start_time': '',
# End time of VOD input to encode.
'end_time': '',
},
],
}
Expand Down Expand Up @@ -81,6 +85,10 @@
'segment_size': 10,
# Forces the use of SegmentTemplate in DASH.
'segment_per_file': True,
# Availability window for live packaging.
'availability_window': 300,
# Presentation delay for live packaging.
'presentation_delay': 30,
'encryption': {
# Enables encryption.
# If disabled, the following settings are ignored.
Expand Down
10 changes: 10 additions & 0 deletions streamer/input_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ def get_language(self):
return self._input['language']
return None

def get_start_time(self):
if 'start_time' in self._input:
return self._input['start_time']
return None

def get_end_time(self):
if 'end_time' in self._input:
return self._input['end_time']
return None

def has_video(self):
if 'input_type' in self._input:
if (self._input['input_type'] == 'webcam' or
Expand Down
29 changes: 15 additions & 14 deletions streamer/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def __init__(self, aac_bitrate, opus_bitrate):

class ResolutionData():

def __init__(self, height, h264_bitrate, vp9_bitrate, h264_profile):
def __init__(self, width, height, h264_bitrate, vp9_bitrate, h264_profile):
self.width = width
self.height = height
self.h264_bitrate = h264_bitrate
self.vp9_bitrate = vp9_bitrate
Expand All @@ -48,19 +49,19 @@ def __ge__(self, other):
# A map of resolutions to ResolutionData objects which contain
# the height and H264 bitrate of a given resolution.
RESOLUTION_MAP = {
'144p': ResolutionData(144, '108k', '95k', 'baseline'),
'240p': ResolutionData(240, '242k', '150k', 'main'),
'360p': ResolutionData(360, '400k', '276k', 'main'),
'480p': ResolutionData(480, '2M', '750k', 'main'),
'576p': ResolutionData(576, '2.5M', '1M', 'main'),
'720p': ResolutionData(720, '3M', '2M', 'main'),
'720p-hfr': ResolutionData(720, '4M', '4M', 'main'),
'1080p': ResolutionData(1080, '5M', '4M', 'high'),
'1080p-hfr': ResolutionData(1080, '6M', '6M', 'high'),
'2k': ResolutionData(1440, '9M', '6M', 'high'),
'2k-hfr': ResolutionData(1440, '14M', '9M', 'high'),
'4k': ResolutionData(2160, '17M', '12M', 'uhd'),
'4k-hfr': ResolutionData(2160, '25M', '18M', 'uhd'),
'144p': ResolutionData(256, 144, '108k', '95k', 'baseline'),
'240p': ResolutionData(426, 240, '242k', '150k', 'main'),
'360p': ResolutionData(640, 360, '400k', '276k', 'main'),
'480p': ResolutionData(854, 480, '2M', '750k', 'main'),
'576p': ResolutionData(1024, 576, '2.5M', '1M', 'main'),
'720p': ResolutionData(1280, 720, '3M', '2M', 'main'),
'720p-hfr': ResolutionData(1280, 720, '4M', '4M', 'main'),
'1080p': ResolutionData(1920, 1080, '5M', '4M', 'high'),
'1080p-hfr': ResolutionData(1920, 1080, '6M', '6M', 'high'),
'2k': ResolutionData(2560, 1440, '9M', '6M', 'high'),
'2k-hfr': ResolutionData(2560, 1440, '14M', '9M', 'high'),
'4k': ResolutionData(3840, 2160, '17M', '12M', 'uhd'),
'4k-hfr': ResolutionData(3840, 2160, '25M', '18M', 'uhd'),
}

class Metadata():
Expand Down
7 changes: 6 additions & 1 deletion streamer/packager_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,14 @@ def start(self):
if self._config.mode == 'live':
args += [
# Number of seconds the user can rewind through backwards.
'--time_shift_buffer_depth', '300',
'--time_shift_buffer_depth',
str(self._config.packager['availability_window']),
# Number of segments preserved outside the current live window.
'--preserved_segments_outside_live_window', '1',
# Number of seconds of content encoded/packaged that is ahead of the
# live edge.
'--suggested_presentation_delay',
str(self._config.packager['presentation_delay']),
]

args += self._setup_manifest_format()
Expand Down
11 changes: 11 additions & 0 deletions streamer/transcoder_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ def start(self):
'-i', input.get_name(),
]

if input.get_start_time():
args += [
# Encode from intended starting time of the VOD input.
'-ss', input.get_start_time(),
]
if input.get_end_time():
args += [
# Encode until intended ending time of the VOD input.
'-to', input.get_end_time(),
]

# Check if the media type of the input is audio and if there are expected
# outputs for the audio input.
if input.get_media_type() == 'audio' and self._output_audios:
Expand Down
137 changes: 133 additions & 4 deletions tests/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const dashManifestUrl = flaskServerUrl + 'output_files/output.mpd';
const hlsManifestUrl = flaskServerUrl + 'output_files/master_playlist.m3u8';
const TEST_DIR = 'test_assets/';
let player;
let video;

async function startStreamer(inputConfig, pipelineConfig) {
// Send a request to flask server to start Shaka Streamer.
Expand Down Expand Up @@ -45,8 +46,6 @@ async function stopStreamer() {
}

describe('Shaka Streamer', () => {
let video;

beforeAll(() => {
shaka.polyfill.installAll();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 400 * 1000;
Expand Down Expand Up @@ -89,6 +88,14 @@ describe('Shaka Streamer', () => {
// TODO: Redo this test with both 5.1 and stereo once 5.1 is supported.
channelsTests(hlsManifestUrl, '(hls)');
channelsTests(dashManifestUrl, '(dash)');
// The HLS manifest does not indicate the availability window, so only run a
// DASH test for this case.
availabilityTests(dashManifestUrl, '(dash)');
// There is no option supported by Packager to set a presentation delay with
// HLS, so only run a dash test for this case.
delayTests(dashManifestUrl, '(dash)');
durationTests(hlsManifestUrl, '(hls)');
durationTests(dashManifestUrl, '(dash)');
});

function resolutionTests(manifestUrl, format) {
Expand Down Expand Up @@ -386,7 +393,7 @@ function textTracksTests(manifestUrl, format) {
}

function vodTests(manifestUrl, format) {
it('has a vod streaming mode', async () => {
it('has a vod streaming mode ' + format, async () => {
const inputConfigDict = {
// List of inputs. Each one is a dictionary.
'inputs': [
Expand Down Expand Up @@ -425,7 +432,7 @@ function vodTests(manifestUrl, format) {
}

function channelsTests(manifestUrl, format) {
it('outputs the correct number of channels', async () => {
it('outputs the correct number of channels ' + format, async () => {
const inputConfigDict = {
// List of inputs. Each one is a dictionary.
'inputs': [
Expand Down Expand Up @@ -463,3 +470,125 @@ function channelsTests(manifestUrl, format) {
expect(trackList[0].channelsCount).toBe(2);
});
}

function availabilityTests(manifestUrl, format) {
it('outputs the correct availability window ' + format, async() => {
const inputConfigDict = {
'inputs': [
{
'name': TEST_DIR + 'Sintel.2010.720p.Small.mkv',
'input_type': 'looped_file',
'media_type': 'video',
'resolution': '720p',
'frame_rate': 24.0,
'track_num': 0,
},
],
};
const pipelineConfigDict = {
'streaming_mode': 'live',
'transcoder': {
'resolutions': [
'144p',
],
'video_codecs': [
'h264',
],
},
'packager': {
'manifest_format': [
'dash',
],
'segment_per_file': true,
'availability_window': 500,
},
};
await startStreamer(inputConfigDict, pipelineConfigDict);
const response = await fetch(manifestUrl);
const bodyText = await response.text();
const re = /timeShiftBufferDepth="([^"]*)"/;
const found = bodyText.match(re);
expect(found).not.toBe(null);
if (found)
expect(found[1]).toBe('PT500S');
});
}

function delayTests(manifestUrl, format) {
it('outputs the correct presentation delay ' + format, async() => {
const inputConfigDict = {
'inputs': [
{
'name': TEST_DIR + 'Sintel.2010.720p.Small.mkv',
'input_type': 'looped_file',
'media_type': 'video',
'resolution': '720p',
'frame_rate': 24.0,
'track_num': 0,
},
],
};
const pipelineConfigDict = {
'streaming_mode': 'live',
'transcoder': {
'resolutions': [
'144p',
],
'video_codecs': [
'h264',
],
},
'packager': {
'manifest_format': [
'dash',
],
'segment_per_file': true,
'presentation_delay': 100,
},
};
await startStreamer(inputConfigDict, pipelineConfigDict);
await player.load(manifestUrl);
delay = player.getManifest().presentationTimeline.getDelay();
expect(delay).toBe(100);
});
}

function durationTests(manifestUrl, format) {
it('outputs the correct duration of video ' + format, async() => {
const inputConfigDict = {
'inputs': [
{
'name': TEST_DIR + 'Sintel.2010.720p.Small.mkv',
'media_type': 'video',
'resolution': '720p',
'frame_rate': 24.0,
'track_num': 0,
'is_interlaced': false,
'start_time': '00:00:00',
'end_time': '00:00:10',
},
],
};
const pipelineConfigDict = {
'streaming_mode': 'vod',
'transcoder': {
'resolutions': [
'144p',
],
'video_codecs': [
'h264',
],
},
'packager': {
'manifest_format': [
'dash',
'hls',
],
'segment_per_file': false,
},
};
await startStreamer(inputConfigDict, pipelineConfigDict);
await player.load(manifestUrl);
expect(video.duration).toBeCloseTo(10);
});
}

0 comments on commit 83d26db

Please sign in to comment.