diff --git a/Earthfile b/Earthfile index 43b3aeb9d..89b4f81b3 100644 --- a/Earthfile +++ b/Earthfile @@ -130,7 +130,7 @@ unit-tests: api-tests: FROM --platform=linux/amd64 bdwyertech/go-crosscompile - RUN apk add npm font-noto && fc-cache -f + RUN apk add ffmpeg npm COPY . /build WORKDIR /build/test/automated/api RUN npm install diff --git a/core/transcoder/transcoder.go b/core/transcoder/transcoder.go index 57dbd4b9f..fe162353b 100644 --- a/core/transcoder/transcoder.go +++ b/core/transcoder/transcoder.go @@ -132,7 +132,7 @@ func (t *Transcoder) Start() { } if err := _commandExec.Start(); err != nil { - log.Errorln("Transcoder error. See", logging.GetTranscoderLogFilePath(), "for full output to debug.") + log.Errorln("Transcoder error. See ", logging.GetTranscoderLogFilePath(), " for full output to debug.") log.Panicln(err, command) } @@ -150,7 +150,7 @@ func (t *Transcoder) Start() { } if err != nil { - log.Errorln("transcoding error. look at", logging.GetTranscoderLogFilePath(), "to help debug. your copy of ffmpeg may not support your selected codec of", t.codec.Name(), "https://owncast.online/docs/codecs/") + log.Errorln("transcoding error. look at ", logging.GetTranscoderLogFilePath(), " to help debug. your copy of ffmpeg may not support your selected codec of", t.codec.Name(), "https://owncast.online/docs/codecs/") } } diff --git a/test/automated/api/configmanagement.test.js b/test/automated/api/configmanagement.test.js index e06109120..0307dbcd9 100644 --- a/test/automated/api/configmanagement.test.js +++ b/test/automated/api/configmanagement.test.js @@ -374,9 +374,10 @@ test('verify updated config values', async (done) => { test('verify admin stream details', async (done) => { const res = await getAdminResponse('status'); - expect(res.body.broadcaster.streamDetails.width).toBe(1280); - expect(res.body.broadcaster.streamDetails.height).toBe(720); - expect(res.body.broadcaster.streamDetails.framerate).toBe(60); + expect(res.body.broadcaster.streamDetails.width).toBe(320); + expect(res.body.broadcaster.streamDetails.height).toBe(180); + expect(res.body.broadcaster.streamDetails.framerate).toBe(24); + expect(res.body.broadcaster.streamDetails.videoBitrate).toBe(1269); expect(res.body.broadcaster.streamDetails.videoCodec).toBe('H.264'); expect(res.body.broadcaster.streamDetails.audioCodec).toBe('AAC'); expect(res.body.online).toBe(true); diff --git a/test/automated/api/run.sh b/test/automated/api/run.sh index 8ac5aff26..59979ffab 100755 --- a/test/automated/api/run.sh +++ b/test/automated/api/run.sh @@ -6,13 +6,14 @@ TEMP_DB=$(mktemp) npm install --quiet --no-progress # Download a specific version of ffmpeg -FFMPEG_PATH=$(mktemp -d) -pushd "$FFMPEG_PATH" >/dev/null || exit -curl -sL --fail https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-linux-64.zip --output ffmpeg.zip >/dev/null -unzip -o ffmpeg.zip >/dev/null -chmod +x ffmpeg -PATH=$FFMPEG_PATH:$PATH -popd >/dev/null || exit +if [ ! -d "ffmpeg" ]; then + mkdir ffmpeg + pushd ffmpeg >/dev/null || exit + curl -sL https://github.com/vot/ffbinaries-prebuilt/releases/download/v4.2.1/ffmpeg-4.2.1-linux-64.zip --output ffmpeg.zip >/dev/null + unzip -o ffmpeg.zip >/dev/null + PATH=$PATH:$(pwd) + popd >/dev/null || exit +fi pushd ../../.. >/dev/null || exit @@ -26,12 +27,12 @@ sleep 5 # Start streaming the test file over RTMP to # the local owncast instance. -../../ocTestStream.sh & +ffmpeg -hide_banner -loglevel panic -stream_loop -1 -re -i ../test.mp4 -vcodec libx264 -profile:v main -sc_threshold 0 -b:v 1300k -acodec copy -f flv rtmp://127.0.0.1/live/abc123 & FFMPEG_PID=$! function finish { + rm "$TEMP_DB" kill $SERVER_PID $FFMPEG_PID - rm -fr "$TEMP_DB" "$FFMPEG_PATH" } trap finish EXIT diff --git a/test/automated/browser/run.sh b/test/automated/browser/run.sh index 2abf95c26..a49ef6189 100755 --- a/test/automated/browser/run.sh +++ b/test/automated/browser/run.sh @@ -22,13 +22,15 @@ npm install --quiet --no-progress popd # Download a specific version of ffmpeg -FFMPEG_PATH=$(mktemp -d) -pushd "$FFMPEG_PATH" >/dev/null || exit -curl -sL --fail https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-linux-64.zip --output ffmpeg.zip >/dev/null -unzip -o ffmpeg.zip >/dev/null -chmod +x ffmpeg -PATH=$FFMPEG_PATH:$PATH -popd >/dev/null || exit +if [ ! -d "ffmpeg" ]; then + echo "Downloading ffmpeg..." + mkdir -p /tmp/ffmpeg + pushd /tmp/ffmpeg >/dev/null + curl -sL --fail https://github.com/vot/ffbinaries-prebuilt/releases/download/v4.2.1/ffmpeg-4.2.1-linux-64.zip --output ffmpeg.zip + unzip -o ffmpeg.zip >/dev/null + PATH=$PATH:$(pwd) + popd >/dev/null +fi # Build and run owncast from source echo "Building owncast..." @@ -47,13 +49,13 @@ npx cypress run --group "mobile-offline" --ci-build-id $BUILD_ID --tag "mobile,o # Start streaming the test file over RTMP to # the local owncast instance. echo "Waiting for stream to start..." -../../ocTestStream.sh & +ffmpeg -hide_banner -loglevel panic -stream_loop -1 -re -i ../test.mp4 -vcodec libx264 -profile:v main -sc_threshold 0 -b:v 1300k -acodec copy -f flv rtmp://127.0.0.1/live/abc123 & STREAMING_CLIENT=$! function finish { echo "Cleaning up..." + rm "$TEMP_DB" kill $SERVER_PID $STREAMING_CLIENT - rm -fr "$TEMP_DB" "$FFMPEG_PATH" } trap finish EXIT SIGHUP SIGINT SIGTERM SIGQUIT SIGABRT SIGTERM diff --git a/test/automated/hls/run.sh b/test/automated/hls/run.sh index c4cf71fc7..861b22284 100755 --- a/test/automated/hls/run.sh +++ b/test/automated/hls/run.sh @@ -2,6 +2,13 @@ set -e +function start_stream() { + # Start streaming the test file over RTMP to + # the local owncast instance. + ffmpeg -hide_banner -loglevel panic -stream_loop -1 -re -i ../test.mp4 -vcodec libx264 -profile:v main -sc_threshold 0 -b:v 1300k -acodec copy -f flv rtmp://127.0.0.1/live/abc123 & + STREAMING_CLIENT=$! +} + function update_storage_config() { echo "Configuring external storage to use ${S3_BUCKET}..." @@ -17,13 +24,14 @@ TEMP_DB=$(mktemp) npm install --silent >/dev/null # Download a specific version of ffmpeg -FFMPEG_PATH=$(mktemp -d) -pushd "$FFMPEG_PATH" >/dev/null || exit -curl -sL --fail https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-linux-64.zip --output ffmpeg.zip >/dev/null -unzip -o ffmpeg.zip >/dev/null -chmod +x ffmpeg -PATH=$FFMPEG_PATH:$PATH -popd >/dev/null || exit +if [ ! -d "ffmpeg" ]; then + mkdir ffmpeg + pushd ffmpeg >/dev/null + curl -sL https://github.com/vot/ffbinaries-prebuilt/releases/download/v4.2.1/ffmpeg-4.2.1-linux-64.zip --output ffmpeg.zip >/dev/null + unzip -o ffmpeg.zip >/dev/null + PATH=$PATH:$(pwd) + popd >/dev/null +fi pushd ../../.. >/dev/null @@ -32,19 +40,18 @@ go build -o owncast main.go ./owncast -database "$TEMP_DB" & SERVER_PID=$! +function finish { + echo "Cleaning up..." + rm "$TEMP_DB" + kill $SERVER_PID $STREAMING_CLIENT +} +trap finish EXIT + popd >/dev/null sleep 5 # Start the stream. -../../ocTestStream.sh & -STREAMING_CLIENT=$! - -function finish { - echo "Cleaning up..." - kill $SERVER_PID $STREAMING_CLIENT - rm -fr "$TEMP_DB" "$FFMPEG_PATH" -} -trap finish EXIT +start_stream echo "Waiting..." sleep 13 diff --git a/test/automated/test.mp4 b/test/automated/test.mp4 new file mode 100644 index 000000000..b5b5e87ff Binary files /dev/null and b/test/automated/test.mp4 differ diff --git a/test/ocTestStream.sh b/test/ocTestStream.sh index 9994b61bf..7cc22677b 100755 --- a/test/ocTestStream.sh +++ b/test/ocTestStream.sh @@ -1,116 +1,55 @@ -#!/bin/bash +#!/usr/bin/env bash -# Requirements: -# ffmpeg (a recent version with loop video support) -# a Sans family font (for overlay text) -# awk -# readlink +# A recent version of ffmpeg is required for the loop of the provided videos +# to repeat indefinitely. +# Example: ./test/ocTestStream.sh ~/Downloads/*.mp4 rtmp://localhost/live/abc123 -# Example: ./test/ocTestStream.sh ~/Downloads/*.mp4 rtmp://127.0.0.1/live/abc123 - - -ffmpeg_execs=( 'ffmpeg' 'ffmpeg.exe' ) -ffmpeg_paths=( './' '../' '' ) - -for _ffmpeg_exec in "${ffmpeg_execs[@]}"; do - for _ffmpeg_path in "${ffmpeg_paths[@]}"; do - if [[ -x "$(command -v "${_ffmpeg_path}${_ffmpeg_exec}")" ]]; then - ffmpeg_exec="${_ffmpeg_path}${_ffmpeg_exec}" - break - fi - done -done - -if [[ ${*: -1} == "--help" ]]; then - echo "ocTestStream is used for sending pre-recorded or internal test content to an RTMP server." - echo "Usage: ./ocTestStream.sh [VIDEO_FILES] [RTMP_DESINATION]" - echo "VIDEO_FILES: path to one or multiple videos for sending to the RTMP server (optional)" - echo "RTMP_DESINATION: URL of RTMP server with key (optional; default: rtmp://127.0.0.1/live/abc123)" +if ! [[ $1 ]] +then + echo "ocTestStream is used for sending pre-recorded content to a RTMP server." + echo "Will default to localhost with the stream key of abc123 if one isn't provided." + echo "./ocTestStream.sh *.mp4 [RTMPDESINATION]" exit -elif [[ ${*: -1} == *"rtmp://"* ]]; then +fi + +# Make the destination optional and point to localhost with default key +if [[ ${*: -1} == *"rtmp://"* ]]; then echo "RTMP server is specified" DESTINATION_HOST=${*: -1} FILE_COUNT=$(( ${#} - 1 )) else echo "RTMP server is not specified" - DESTINATION_HOST="rtmp://127.0.0.1/live/abc123" + DESTINATION_HOST="rtmp://localhost/live/abc123" FILE_COUNT=${#} fi -if [[ -z "$ffmpeg_exec" ]]; then - echo "ERROR: ffmpeg was not found in path or in the current directory! Please install ffmpeg before using this script." - exit 1 -else - ffmpeg_version=$("$ffmpeg_exec" -version | awk -F 'ffmpeg version' '{print $2}' | awk 'NR==1{print $1}') - echo "ffmpeg executable: $ffmpeg_exec ($ffmpeg_version)" - echo "ffmpeg path: $(readlink -f "$(which "$ffmpeg_exec")")" +if [[ FILE_COUNT -eq 0 ]]; then + echo "ERROR: ocTestStream needs a video file for sending to the RTMP server." + exit fi -if [[ ${FILE_COUNT} -eq 0 ]]; then - echo "Streaming internal test video loop to $DESTINATION_HOST" - echo "...press ctrl+c to exit" +CONTENT=${*:1:${FILE_COUNT}} - command "${ffmpeg_exec}" -hide_banner -loglevel panic -nostdin -re -f lavfi \ - -i "testsrc=size=1280x720:rate=60[out0];sine=frequency=400:sample_rate=48000[out1]" \ - -vf "[in]drawtext=fontsize=96: box=1: boxcolor=black@0.75: boxborderw=5: fontcolor=white: x=(w-text_w)/2: y=((h-text_h)/2)+((h-text_h)/-2): text='Owncast Test Stream', drawtext=fontsize=96: box=1: boxcolor=black@0.75: boxborderw=5: fontcolor=white: x=(w-text_w)/2: y=((h-text_h)/2)+((h-text_h)/2): text='%{gmtime\:%H\\\\\:%M\\\\\:%S} UTC'[out]" \ - -nal-hrd cbr \ - -metadata:s:v encoder=test \ - -vcodec libx264 \ - -acodec aac \ - -preset veryfast \ - -profile:v baseline \ - -tune zerolatency \ - -bf 0 \ - -g 0 \ - -b:v 6320k \ - -b:a 160k \ - -ac 2 \ - -ar 48000 \ - -minrate 6320k \ - -maxrate 6320k \ - -bufsize 6320k \ - -muxrate 6320k \ - -r 60 \ - -pix_fmt yuv420p \ - -color_range 1 -colorspace 1 -color_primaries 1 -color_trc 1 \ - -flags:v +global_header \ - -bsf:v dump_extra \ - -x264-params "nal-hrd=cbr:min-keyint=2:keyint=2:scenecut=0:bframes=0" \ - -f flv "$DESTINATION_HOST" - -else - - CONTENT=${*:1:${FILE_COUNT}} - - rm -f list.txt - for file in $CONTENT - do - echo "file '$file'" >> list.txt - done - - function finish { - rm list.txt - } - trap finish EXIT - - echo "Streaming a loop of ${FILE_COUNT} video(s) to $DESTINATION_HOST" - if [[ ${FILE_COUNT} -gt 1 ]]; then - echo "Warning: If these files differ greatly in formats, transitioning from one to another may not always work correctly." - fi - echo "$CONTENT" - echo "...press ctrl+c to exit" - - command "${ffmpeg_exec}" -hide_banner -loglevel panic -nostdin -stream_loop -1 -re -f concat \ - -safe 0 \ - -i list.txt \ - -vcodec libx264 \ - -profile:v high \ - -g 48 \ - -r 24 \ - -sc_threshold 0 \ - -b:v 1300k \ - -preset veryfast \ - -acodec copy \ - -vf drawtext="fontsize=96: box=1: boxcolor=black@0.75: boxborderw=5: fontcolor=white: x=(w-text_w)/2: y=((h-text_h)/2)+((h-text_h)/4): text='%{gmtime\:%H\\\\\:%M\\\\\:%S}'" \ - -f flv "$DESTINATION_HOST" +# Delete the old list of files if it exists +if test -f list.txt; then + rm list.txt fi + +for file in $CONTENT +do + echo "file '$file'" >> list.txt +done + +function finish { + rm list.txt +} +trap finish EXIT + +echo "Streaming a loop of ${FILE_COUNT} videos to $DESTINATION_HOST." +if [[ FILE_COUNT -gt 1 ]]; then + echo "Warning: If these files differ greatly in formats transitioning from one to another may not always work correctly." +fi +echo "$CONTENT" +echo "...press ctl+c to exit" + +ffmpeg -hide_banner -loglevel panic -stream_loop -1 -re -f concat -safe 0 -i list.txt -vcodec libx264 -profile:v high -g 48 -r 24 -sc_threshold 0 -b:v 1300k -preset veryfast -acodec copy -vf drawtext="fontfile=monofonto.ttf: fontsize=96: box=1: boxcolor=black@0.75: boxborderw=5: fontcolor=white: x=(w-text_w)/2: y=((h-text_h)/2)+((h-text_h)/4): text='%{gmtime\:%H\\\\\:%M\\\\\:%S}'" -f flv "$DESTINATION_HOST" diff --git a/test/testContent.sh b/test/testContent.sh new file mode 100755 index 000000000..ab4466dea --- /dev/null +++ b/test/testContent.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +ffmpeg -hide_banner -loglevel panic -re -f lavfi \ + -i "testsrc=size=1280x720:rate=60[out0];sine=frequency=400:sample_rate=48000[out1]" \ + -vf "[in]drawtext=fontsize=96: box=1: boxcolor=black@0.75: boxborderw=5: fontcolor=white: x=(w-text_w)/2: y=((h-text_h)/2)+((h-text_h)/-2): text='Owncast Test Stream', drawtext=fontsize=96: box=1: boxcolor=black@0.75: boxborderw=5: fontcolor=white: x=(w-text_w)/2: y=((h-text_h)/2)+((h-text_h)/2): text='%{gmtime\:%H\\\\\:%M\\\\\:%S} UTC'[out]" \ + -nal-hrd cbr \ + -metadata:s:v encoder=test \ + -vcodec libx264 \ + -acodec aac \ + -preset veryfast \ + -profile:v baseline \ + -tune zerolatency \ + -bf 0 \ + -g 0 \ + -b:v 6320k \ + -b:a 160k \ + -ac 2 \ + -ar 48000 \ + -minrate 6320k \ + -maxrate 6320k \ + -bufsize 6320k \ + -muxrate 6320k \ + -r 60 \ + -pix_fmt yuv420p \ + -color_range 1 -colorspace 1 -color_primaries 1 -color_trc 1 \ + -flags:v +global_header \ + -bsf:v dump_extra \ + -x264-params "nal-hrd=cbr:min-keyint=2:keyint=2:scenecut=0:bframes=0" \ + -f flv "rtmp://127.0.0.1/live/abc123" >/dev/null diff --git a/web/docs/README.md b/web/docs/README.md index 290c7d822..97a777e78 100644 --- a/web/docs/README.md +++ b/web/docs/README.md @@ -11,7 +11,7 @@ ### Form fields -- Feel free to use the pre-styled `` text form field or the `` component, in a group of form fields together. These have been styled and laid out to match each other. +- Feel free to use the pre-styled `` text form field or the `` compnent, in a group of form fields together. These have been styled and laid out to match each other. - `Slider`'s - If your form uses an Ant Slider component, follow this recommended markup of CSS classes to maintain a consistent look and feel to other Sliders in the app. ```