Skip to content
Snippets Groups Projects
Commit 7f880726 authored by Julian Rother's avatar Julian Rother
Browse files

Completed livestream model and made live sources page visible

parent b164ce7e
No related branches found
No related tags found
No related merge requests found
......@@ -62,7 +62,7 @@ def restart_failed_live_transcode(id, type, data, state, status):
restart_job(id)
@app.route('/internal/streaming')
#@register_navbar('Streaming', icon='transfer')
@register_navbar('Streaming', icon='broadcast-tower', iconlib='fa')
@mod_required
def streaming():
sources = query('SELECT * FROM live_sources WHERE NOT deleted')
......@@ -152,3 +152,65 @@ def streamauth(server):
modify('UPDATE live_sources SET server = NULL, clientid = NULL, preview_key = NULL, last_active = ? WHERE server = ? AND clientid = ?', datetime.now(), server, request.values['clientid'])
return 'Ok', 200
return 'Bad request', 400
def schedule_livestream(lecture_id):
def build_filter(l):
return ','.join(l) if l else None
server = 'rwth.video'
lecture = query('SELECT * FROM lectures WHERE id = ?', lecture_id)[0]
settings = json.loads(lecture['stream_settings'])
data = {'src1': {'afilter': [], 'vfilter': []}, 'src2': {'afilter': [], 'vfilter': []}, 'afilter': [], 'videoag_logo': int(bool(settings.get('video_showlogo'))), 'lecture_id': lecture['id']}
src1 = (query('SELECT * FROM live_sources WHERE NOT deleted AND id = ?', settings.get('source1')) or [{}])[0]
src2 = (query('SELECT * FROM live_sources WHERE NOT deleted AND id = ?', settings.get('source2')) or [{}])[0]
for idx, obj in zip([1,2], [src1, src2]):
if obj:
server = obj['server']
data['src%i'%idx]['url'] = 'rtmp://%s/src/%i'%(obj['server'], obj['id'])
mode = settings.get('source%i_audiomode'%idx)
leftvol = float(settings.get('source%i_leftvolume'%idx, 100))/100.0
rightvol = float(settings.get('source%i_rightvolume'%idx, 100))/100.0
if mode == 'mono':
data['src%i'%idx]['afilter'].append('pan=mono|c0=%f*c0+%f*c1'%(0.5*leftvol, 0.5*rightvol))
elif mode == 'stereo':
data['src%i'%idx]['afilter'].append('pan=stereo|c0=%f*c0|c1=%f*c1'%(leftvol, rightvol))
elif mode == 'unchanged':
pass
elif mode == 'off':
data['src%i'%idx]['afilter'].append('pan=mono|c0=0*c0')
else:
raise(Exception())
mode = settings.get('videomode')
if mode == '1':
data['vmix'] = 'streamselect=map=0'
elif mode == '2':
data['vmix'] = 'streamselect=map=1'
elif vmode == 'lecture4:3':
data['src1']['vfilter'].append('scale=1440:1080')
data['src2']['vfilter'].append('scale=1440:810,pad=1440:1080:0:135,crop=480:1080')
data['vmix'] = 'hstack'
elif vmode == 'lecture16:9':
data['src1']['vfilter'].append('scale=1440:810,pad=1440:1080:0:135')
data['src2']['vfilter'].append('scale=1440:810,pad=1440:1080:0:135,crop=480:1080')
data['vmix'] = 'hstack'
elif vmode == 'sidebyside':
data['src1']['vfilter'].append('scale=960:540')
data['src2']['vfilter'].append('scale=960:540')
data['vmix'] = 'hstack,pad=1920:1080:0:270'
if settings.get('audio_normalize'):
data['afilter'].append('loudnorm')
data['afilter'] = build_filter(data['afilter'])
data['src1']['afilter'] = build_filter(data['src1']['afilter'])
data['src1']['vfilter'] = build_filter(data['src1']['vfilter'])
data['src2']['afilter'] = build_filter(data['src2']['afilter'])
data['src2']['vfilter'] = build_filter(data['src2']['vfilter'])
data['destbase'] = 'rtmp://%s/hls/l_%i'%(server, lecture['id'])
job_id = schedule_job('complex_live_transcode', data, priority=10)
return job_id
@app.route('/internal/streaming/start', methods=['POST'])
@mod_required
def start_stream():
lecture_id = int(request.values['lecture_id'])
course = (query('SELECT courses.* FROM courses JOIN lectures ON (courses.id = lectures.course_id) WHERE lectures.id = ?', lecture_id) or [None])[0]
schedule_livestream(lecture_id)
return redirect(url_for('course', handle=course['handle']))
static/smptebars.jpg

6.36 KiB

......@@ -160,12 +160,21 @@
<option value="{{ source.id }}">{{ source.name }}</option>
{% endfor %}
</select>
<img src="{{ config.VIDEOPREFIX }}/thumbnail/s_none.jpg" style="width: 100%; margin-bottom: 0.5em; margin-top: 0.5em"/>
<select name="source{{ snum }}_audiomode" class="form-control">
<option selected value="unchanged">Audio unverändert</option>
<option value="left">Nur linke Tonspur</option>
<option value="right">Nur rechte Tonspur</option>
<option value="mix">Monomix aller Tonspuren</option>
<img src="{{ url_for('static', filename='smptebars.jpg') }}" style="width: 100%; margin-bottom: 0.5em; margin-top: 0.5em"/>
<label>Lautstärke</label>
<div class="row">
<div class="col-xs-6">
<input type="range" name="source{{ snum }}_leftvolume" value="100">
</div>
<div class="col-xs-6">
<input type="range" name="source{{ snum }}_rightvolume" value="100">
</div>
</div>
<select name="source{{ snum }}_audiomode" class="form-control" style="margin-top: 0.5em">
<option value="mono" selected>Mono-Mix</option>
<option value="stereo">Stereo</option>
<option value="unchanged">Audio unverändert</option>
<option value="off">Kein Audio</option>
</select>
</div>
{% endfor %}
......@@ -174,36 +183,27 @@
<select name="videomode" class="form-control">
<option value="1" selected>Nur Quelle 1</option>
<option value="2">Nur Quelle 2</option>
<option value="sidebyside">Side-by-Side (Quelle 1 groß, 1/3 von 2 daneben)</option>
<option value="lecture4:3">Quelle 1 (4:3) links, Ausschnitt von 2 rechts</option>
<option value="lecture16:9">Quelle 1 (16:9) links, Ausschnitt von 2 rechts</option>
<option value="sidebyside">Side-by-Side (Quelle 1 links, 2 rechts)</option>
</select>
<div class="checkbox"><label><input name="video_showlogo" type="checkbox" checked>Video AG-Logo einblenden</label></div>
</div>
<div class="col-xs-12" style="margin-top: 1em">
<label>Audio</label>
<select name="audiomode" class="form-control">
<option value="1" selected>Quelle 1</option>
<option value="2">Quelle 2</option>
<option value="splitstereo">Quelle 1 links, 2 rechts</option>
<option value="mix">Mix beider Quellen</option>
</select>
<label>Weitere Einstellungen</label>
<div style="margin-top: -1em;">
<div class="checkbox"><label><input name="audio_normalize" type="checkbox">Lautstärke normalisieren</label></div>
</div>
<div class="col-xs-12" style="margin-top: 1em">
<label>Ausgabeformat</label>
<select name="outputmode" class="form-control">
<option selected value="multi">1080p/720p/360p (Standard)</option>
<option value="720p">Nur 720p</option>
</select>
</div>
<div class="col-xs-12" style="margin-top: 1em">
<label>Weitere Einstellungen</label>
<div class="checkbox"><label><input name="autostart" type="checkbox" checked>Automatisch starten</label></div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<form class="form-inline" method="post" action="{{ url_for('start_stream') }}">
<input type="hidden" id="editstream-lectureid" name="lecture_id" value="">
<button type="submit" id="editstream-start" class="btn btn-danger">Speichern und starten</button>
<button type="button" id="editstream-submit" class="btn btn-primary">Speichern</button>
</form>
</div>
</div>
</div>
......@@ -212,7 +212,10 @@
<script>
function editstream_update() {
$('#editstream .source-select').each(function () {
if ($(this).val())
$(this).siblings('img').prop('src', '{{ config.VIDEOPREFIX }}/thumbnail/s_'+$(this).val()+'.jpg');
else
$(this).siblings('img').prop('src', {{ url_for('static', filename='smptebars.jpg')|tojson }});
});
};
function editstream_dump() {
......@@ -223,6 +226,9 @@ function editstream_dump() {
$("#editstream select[name!='']").each(function () {
res[$(this).attr('name')] = $(this).val();
});
$("#editstream input[type='range'][name!='']").each(function () {
res[$(this).attr('name')] = $(this).val();
});
return res;
};
function editstream_load(obj) {
......@@ -234,15 +240,24 @@ function editstream_load(obj) {
if ($(this).attr('name') in obj)
$(this).val(obj[$(this).attr('name')]);
});
$("#editstream input[type='range'][name!='']").each(function () {
if ($(this).attr('name') in obj)
$(this).val(obj[$(this).attr('name')]);
});
};
$('#editstream .source-select').on('change', editstream_update);
$('#editstream-submit').on('click', function () {
moderator.api.set($('#editstream').data('currentpath'), JSON.stringify(editstream_dump()), true);
$('#editstream').modal('hide');
});
$('#editstream-start').on('click', function () {
moderator.api.set($('#editstream').data('currentpath'), JSON.stringify(editstream_dump()), true);
return true;
});
$('#editstream').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget);
$('#editstream').data('currentpath', button.data('path'));
$('#editstream-lectureid').val(button.data('lectureid'));
$("#editstream-form")[0].reset();
if (button.data('value'))
editstream_load(button.data('value'));
......
......@@ -262,7 +262,7 @@ $('#embedcodebtn').popover(
</li>
{% if ismod() %}
<li class="pull-right">
<button class="btn btn-default" data-toggle="modal" data-target="#editstream" data-path="{{ 'lectures.%i.stream_settings'%lecture.id }}" data-value='{{ lecture.stream_settings|e }}'>
<button class="btn btn-default" data-toggle="modal" data-target="#editstream" data-lectureid="{{ lecture.id }}" data-path="{{ 'lectures.%i.stream_settings'%lecture.id }}" data-value='{{ lecture.stream_settings|e }}'>
<span class="fas fa-broadcast-tower"></span>
</button>
</li>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment