diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ff74a19 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,44 @@ +# Ignore environment files with secrets +.env +**/.env +*.env +node/.env +base/.env + +# Ignore git repository +.git +.gitignore +.gitattributes + +# Ignore node modules (if any) +**/node_modules + +# Ignore security-sensitive files +**/*.pem +**/*.key +**/*.crt +**/*.p12 +**/*.pfx +**/*.jks + +# Ignore backup and temporary files +**/*.bak +**/*.swp +**/*.swo +**/*~ +**/.DS_Store + +# Ignore IDE and editor files +.vscode/ +.idea/ +*.code-workspace + +# Ignore CI/CD artifacts (already in repo, but exclude from build context) +.github/ + +# Ignore documentation +*.md +!node/README.md + +# Keep .env.example (it's a template, not secrets) +!**/.env.example \ No newline at end of file diff --git a/.github/workflows/build-container.yml b/.github/workflows/build-container.yml new file mode 100644 index 0000000..8d79c18 --- /dev/null +++ b/.github/workflows/build-container.yml @@ -0,0 +1,182 @@ +name: Build and Test Orcanode Container + +on: + push: + branches: + - main + - container-release + tags: + - 'v*.*.*' # Trigger on version tags. + pull_request: + paths: + - 'node/**' + - 'base/**' + - '.github/workflows/build-container.yml' + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Build container image + run: docker build -t orcanode:test -f node/Dockerfile . + + - name: Verify Python is available + run: docker run --rm orcanode:test python3 --version + + - name: Verify FFmpeg is installed + run: docker run --rm orcanode:test ffmpeg -version + + - name: Verify Jack audio is installed + run: docker run --rm orcanode:test which jackd + + - name: Verify Python dependencies + run: docker run --rm orcanode:test python3 -c "import boto3; import inotify; print('Python deps OK')" + + - name: Verify application scripts are present + run: docker run --rm orcanode:test ls -la /app/stream.sh + + - name: Test container startup with dev-virt-s3 + run: | + # Copy .env.example to test.env and customize for CI. + cp node/.env.example test.env + + # Replace FILLTHISIN placeholders with test values. + sed -i 's/AWS_ACCESS_KEY_ID=FILLTHISIN/AWS_ACCESS_KEY_ID=test-key-id/g' test.env + sed -i 's/AWS_SECRET_ACCESS_KEY=FILLTHISIN/AWS_SECRET_ACCESS_KEY=test-secret/g' test.env + sed -i 's/key="FILLTHISIN"/key="test-logdna-key"/g' test.env + sed -i 's/NODE_NAME=rpi_location/NODE_NAME=rpi_github_ci/g' test.env + + # Use dev-virt-s3 mode for CI (no audio hardware required). + sed -i 's/NODE_TYPE=hls-only/NODE_TYPE=dev-virt-s3/g' test.env + sed -i 's/NODE_LOOPBACK=true/NODE_LOOPBACK=false/g' test.env + + # Start container with env file. + docker run -d --name orcanode-test-virt \ + --env-file test.env \ + orcanode:test + + sleep 10 + echo "=== Container logs ===" + docker logs orcanode-test-virt + + # Check container health: accept running or cleanly exited (exit code 0). + STATUS=$(docker inspect --format='{{.State.Status}}' orcanode-test-virt) + EXIT_CODE=$(docker inspect --format='{{.State.ExitCode}}' orcanode-test-virt) + + echo "Container status: $STATUS, exit code: $EXIT_CODE" + + if [ "$STATUS" = "running" ]; then + echo "Container is running" + + # Verify HLS segments are being generated. + docker exec orcanode-test-virt ls -la /tmp/rpi_github_ci/hls/ + + echo "Container is generating HLS stream" + elif [ "$STATUS" = "exited" ] && [ "$EXIT_CODE" = "0" ]; then + echo "Container exited cleanly (exit code 0)" + else + echo "Container failed with status: $STATUS, exit code: $EXIT_CODE" + exit 1 + fi + + docker stop orcanode-test-virt || true + docker rm orcanode-test-virt + rm test.env + + - name: Test container startup with hls-only (hardware path) + run: | + # Copy .env.example to test.env and customize for CI. + cp node/.env.example test.env + + # Replace FILLTHISIN placeholders with test values. + sed -i 's/AWS_ACCESS_KEY_ID=FILLTHISIN/AWS_ACCESS_KEY_ID=test-key-id/g' test.env + sed -i 's/AWS_SECRET_ACCESS_KEY=FILLTHISIN/AWS_SECRET_ACCESS_KEY=test-secret/g' test.env + sed -i 's/key="FILLTHISIN"/key="test-logdna-key"/g' test.env + sed -i 's/NODE_NAME=rpi_location/NODE_NAME=rpi_github_ci_hw/g' test.env + + # Keep default NODE_TYPE=hls-only (tests hardware audio path) + + # Start container with env file. + docker run -d --name orcanode-test-hw \ + --env-file test.env \ + orcanode:test + + sleep 5 + echo "=== Container logs (hls-only mode) ===" + docker logs orcanode-test-hw + + # Check container health + STATUS=$(docker inspect --format='{{.State.Status}}' orcanode-test-hw) + EXIT_CODE=$(docker inspect --format='{{.State.ExitCode}}' orcanode-test-hw) + + echo "Container status: $STATUS, exit code: $EXIT_CODE" + + if [ "$STATUS" = "running" ]; then + echo "✓ Container is running (hls-only mode, audio hardware errors expected)" + elif [ "$STATUS" = "exited" ] && [ "$EXIT_CODE" = "0" ]; then + echo "✓ Container exited cleanly (exit code 0)" + else + echo "✗ Container failed with status: $STATUS, exit code: $EXIT_CODE" + exit 1 + fi + + docker stop orcanode-test-hw || true + docker rm orcanode-test-hw + rm test.env + + build-and-push: + needs: test + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/container-release' || github.ref_type == 'tag' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + actions: write # Required for cache-to: type=gha + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + with: + images: ghcr.io/${{ github.repository }}/orcanode + tags: | + # Semver tags on release + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=ref,event=tag + # Branch-based tags + type=ref,event=branch + type=sha + # Latest tag for main branch + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push multi-arch images + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + with: + context: . + file: ./node/Dockerfile + platforms: linux/amd64,linux/arm/v7,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 2eea525..111e681 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.env \ No newline at end of file +.env +.vs diff --git a/README.md b/README.md index 9339a20..1a201ed 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ An ARM or X86 device with a sound card (or other audio input devices) connected ### Installing -Create a base docker image for your architecture by running the script in /base/rpi or /base/amd64 as appropriate. You will need to create a .env file as appropriate for your projects. Here is an example of an .env file (tested/working as of June, 2021)... +The container is built from `node/Dockerfile` and is self-contained (no separate base image step required). You will need to create a `.env` file based on `node/.env.example` for your deployment. Here is an example of an `.env` file... ``` AWS_METADATA_SERVICE_TIMEOUT=5 @@ -43,11 +43,11 @@ LC_ALL=C.UTF-8 ... except that the following fields are excised and will need to be added if you are integrating with the audio and logging systems of Orcasound: ``` -AWSACCESSKEYID=YourAWSaccessKey -AWSSECRETACCESSKEY=YourAWSsecretAccessKey - -SYSLOG_URL=syslog+tls://syslog-a.logdna.com:YourLogDNAPort -SYSLOG_STRUCTURED_DATA='logdna@YourLogDNAnumber key="YourLogDNAKey" tag="docker" +AWS_ACCESS_KEY_ID=YourAWSaccessKeyId +AWS_SECRET_ACCESS_KEY=YourAWSsecretAccessKey + +SYSLOG_URL=syslog://syslog-a.logdna.com:YourLogDNAPort +SYSLOG_STRUCTURED_DATA='logdna@YourLogDNAnumber key="YourLogDNAKey" tag="docker"' ``` (You can request keys via the #hydrophone-nodes channel in the Orcasound Slack. As of October, 2021, we are continuing to use AWS S3 for storage and LogDNA for live-logging and troubleshooting.) @@ -64,7 +64,7 @@ Here are explanations of some of the .env fields: ## Running local tests -At the root of the repository directory (where you also put your .env file) first copy the compose file you want to `docker-compose.yml`. For example, if you have a Raspberry Pi and you want to use the prebuilt image, then copy `docker-compose.rpi-pull.yml` to `docker-compose.yml`. Then run `docker-compose up -d`. Watch what happens using `htop`. If you want to verify files are being written to /tmp or /mnt directories, get the name of your streaming service using `docker-compose ps` (in this case `orcanode_streaming_1`) and then do `docker exec -it orcanode_streaming_1 /bin/bash` to get a bash shell within the running container. +Go to the `node/` directory (where you also put your `.env` file). Copy the compose file you want to `docker-compose.yml`. For example, if you have a Raspberry Pi and you want to use the prebuilt image, then copy `docker-compose.rpi-pull.yml` to `docker-compose.yml`. Then run `docker-compose up -d`. Watch what happens using `htop`. If you want to verify files are being written to /tmp or /mnt directories, get the name of your streaming service using `docker-compose ps` (in this case `orcanode_streaming_1`) and then do `docker exec -it orcanode_streaming_1 /bin/bash` to get a bash shell within the running container. ### Running an end-to-end test @@ -91,9 +91,9 @@ If you would like to add a node to the Orcasound hydrophone network, contact adm ## Built With -* [FFmpeg](https://www.ffmpeg.org/) - Uses ALSA to acquire audio data, then generates lossy streams and/or lossless archive files -* [rsync](https://rsync.samba.org/) - Transfers files locally from /tmp to /mnt directories -* [s3fs](https://github.com/s3fs-fuse/s3fs-fuse) - Used to transfer audio data from local device to S3 bucket(s) +* [FFmpeg](https://www.ffmpeg.org/) - Uses JACK/ALSA to acquire audio data, then generates lossy streams and/or lossless archive files +* [JACK](https://jackaudio.org/) - Low-latency audio server used to route audio from the sound card to FFmpeg +* [boto3](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) - AWS SDK for Python, used to transfer audio data from the local device to S3 bucket(s) ## Contributing diff --git a/base/upload_flac_s3.py b/base/upload_flac_s3.py index 9e7ea6a..b07fa9f 100644 --- a/base/upload_flac_s3.py +++ b/base/upload_flac_s3.py @@ -44,7 +44,7 @@ print("using dev bucket") BUCKET = "dev-streaming-orcasound-net" - log.debug("archive bucket set to ", BUCKET) + log.debug("archive bucket set to %s", BUCKET) def s3_copy_file(path, filename): log.debug('uploading file '+filename+' from '+path+' to bucket '+BUCKET) diff --git a/base/upload_s3.py b/base/upload_s3.py index 56e9a31..511eef2 100644 --- a/base/upload_s3.py +++ b/base/upload_s3.py @@ -44,7 +44,7 @@ print("using dev bucket") BUCKET = "dev-streaming-orcasound-net" - log.debug("hls bucket set to ", BUCKET) + log.debug("hls bucket set to %s", BUCKET) def s3_copy_file(path, filename): log.debug('uploading file '+filename+' from '+path+' to bucket '+BUCKET) diff --git a/node/.env.example b/node/.env.example new file mode 100644 index 0000000..c2963fb --- /dev/null +++ b/node/.env.example @@ -0,0 +1,91 @@ +# Orcanode Configuration Template +# Copy this file to .env and customize for your deployment +# See README.md for detailed field descriptions + +# === AWS Credentials === +# Get these from AWS IAM Console or contact Orcasound admin +AWS_ACCESS_KEY_ID=FILLTHISIN +AWS_SECRET_ACCESS_KEY=FILLTHISIN + +# === AWS Configuration === +AWS_METADATA_SERVICE_TIMEOUT=5 +AWS_METADATA_SERVICE_NUM_ATTEMPTS=0 +REGION=us-west-2 + +# === S3 Bucket Selection === +BUCKET_TYPE=dev +#BUCKET_TYPE=prod + +# === Node Configuration === +# Uncomment the NODE_TYPE you need: +NODE_TYPE=hls-only +#NODE_TYPE=research +#NODE_TYPE=debug +#NODE_TYPE=dev-virt-s3 + +# Unique identifier for this node (format: rpi_location) +NODE_NAME=rpi_location + +# Audio loopback for testing (true/false/hls) +# - true: Route audio input directly to output via Jack +# - false: No loopback +# - hls: Play back the generated HLS stream with ffplay (for testing) +NODE_LOOPBACK=true +#NODE_LOOPBACK=false +#NODE_LOOPBACK=hls + +# Audio Settings +SAMPLE_RATE=48000 +AUDIO_HW_ID=pisound +CHANNELS=1 + +# === Logging Configuration === +# Used by logspout sidecar - get credentials from Orcasound admin +SYSLOG_URL=syslog://syslog-a.logdna.com:37043 +SYSLOG_STRUCTURED_DATA='logdna@48950 key="FILLTHISIN" tag="docker"' + +# === Stream Configuration === +FLAC_DURATION=30 +SEGMENT_DURATION=10 + +# === System Configuration === +LC_ALL=C.UTF-8 + +# === Field Descriptions === +# +# AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY: +# AWS credentials for S3 bucket access +# +# BUCKET_TYPE: +# - dev: Development bucket (dev-streaming-orcasound-net) +# - prod: Production bucket (audio-orcasound-net) +# - custom: Use custom buckets (requires BUCKET_STREAMING and BUCKET_ARCHIVE) +# +# NODE_TYPE: +# - hls-only: Stream HLS segments only (most common) +# - research: Stream HLS + archive FLAC files +# - debug: Stream DASH via mpegts for debugging (requires test-engine-live-tools) +# - dev-virt-s3: Development mode using sample WAV file (no audio hardware required) +# +# NODE_NAME: +# Unique identifier in format: rpi_location (e.g., rpi_bush_point, rpi_port_townsend) +# +# AUDIO_HW_ID: +# Sound card identifier. Find with: arecord -l +# Use logical name (pisound, USB) not device numbers (0,0) +# +# SAMPLE_RATE: +# Audio sampling rate in Hz (48000 for standard, 192000 for research) +# +# CHANNELS: +# Number of audio channels (1=mono, 2=stereo) +# +# SEGMENT_DURATION: +# Seconds per HLS segment (typically 10) +# +# FLAC_DURATION: +# Seconds per archived FLAC file (research mode only, typically 30) +# +# SYSLOG_URL / SYSLOG_STRUCTURED_DATA: +# LogDNA/Mezmo credentials for centralized logging via logspout +# Request credentials in #hydrophone-nodes on Orcasound Slack diff --git a/node/.gitignore b/node/.gitignore new file mode 100644 index 0000000..9d27806 --- /dev/null +++ b/node/.gitignore @@ -0,0 +1,5 @@ +# Environment variables with secrets +.env + +# But keep the example +!.env.example \ No newline at end of file diff --git a/node/Dockerfile b/node/Dockerfile index 20b0ce9..ac097bd 100644 --- a/node/Dockerfile +++ b/node/Dockerfile @@ -1,37 +1,58 @@ # Node Dockerfile for hydrophone streaming -# use base image for project - -FROM orcastream/orcabase:latest -MAINTAINER Orcasound - -###### hack to get ffmpeg to build -# RUN apt-get update && apt-get install -y --no-install-recommends libraspberrypi-dev raspberrypi-kernel-headers -# RUN git clone https://github.com/raspberrypi/userland.git -# RUN cd userland/host_applications/linux/apps/hello_pi && ./rebuild.sh - - -###### Install Jack ################################# - -ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y --no-install-recommends jack-capture -RUN apt-get update && apt-get install -y --no-install-recommends jackd1 - -# Install ALSA and GPAC -#RUN apt-get update && apt-get install -y --no-install-recommends \ -# alsa-utils \ -# gpac - -############################### Copy files ##################################### - -COPY . . - -################################## TODO ######################################## -# Do the following: -# - Add pisound driver curl command -# - Add other audio drivers and configure via CLI if possible? -# - Remove "misc tools" and other installs no longer needed (upon Resin.io deployment)? - -################################# Miscellaneous ################################ - -# Clean up APT when done. -RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +# Multi-arch self-contained build for CI/CD deployment + +FROM python:3.11-slim-bookworm + +LABEL org.opencontainers.image.source="https://github.com/orcasound/orcanode" +LABEL org.opencontainers.image.description="Orcasound hydrophone streaming node" +LABEL org.opencontainers.image.authors="Orcasound " + +# Install system dependencies +RUN export DEBIAN_FRONTEND=noninteractive \ + && apt-get update && apt-get install -y --no-install-recommends \ + ffmpeg \ + jack-capture \ + jackd2 \ + alsa-utils \ + git \ + curl \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Install Python dependencies +RUN pip3 install --no-cache-dir \ + boto3 \ + inotify + +# Set working directory +WORKDIR /app + +# Copy node application files first +COPY node/ ./ + +# Copy Python upload scripts from base directory (overwrites if they exist in node/) +COPY base/upload_s3.py ./ +COPY base/upload_flac_s3.py ./ + +# Set environment defaults +ENV SAMPLE_RATE=48000 \ + STREAM_RATE=48000 \ + CHANNELS=1 \ + NODE_TYPE=hls-only + +# Make scripts executable +RUN chmod +x *.sh + +# Ensure .env.example is available for reference +RUN if [ -f .env.example ]; then \ + echo ".env.example is available at /app/.env.example"; \ + else \ + echo "Warning: .env.example not found"; \ + fi + +# Configure audio group limits for Jack +RUN echo '@audio - memlock 256000' >> /etc/security/limits.conf && \ + echo '@audio - rtprio 75' >> /etc/security/limits.conf + +# Run the streaming script +CMD ["./stream.sh"] diff --git a/node/stream.sh b/node/stream.sh index c929c3d..b55ff8a 100755 --- a/node/stream.sh +++ b/node/stream.sh @@ -27,18 +27,37 @@ mkdir -p /tmp/$NODE_NAME/hls/$timestamp # Output timestamp for this (latest) stream echo $timestamp > /tmp/$NODE_NAME/latest.txt -STREAM_RATE=48000 +if [ -z "${STREAM_RATE}" ]; then + echo "setting stream rate to 48000" + STREAM_RATE=48000 +else + echo "stream rate is set to $STREAM_RATE" +fi -if [ -z ${SAMPLE_RATE+48000}]; then +if [ -z "${SAMPLE_RATE}" ]; then echo "setting sampling rate to 48000" + SAMPLE_RATE=48000 else echo "sample rate is set to $SAMPLE_RATE"; fi -# Setup jack -echo @audio - memlock 256000 >> /etc/security/limits.conf -echo @audio - rtprio 75 >> /etc/security/limits.co -JACK_NO_AUDIO_RESERVATION=1 jackd -t 2000 -P 75 -d alsa -d hw:$AUDIO_HW_ID -r $SAMPLE_RATE -p 1024 -n 10 -s & +# Check if we can set process priority (requires CAP_SYS_NICE) +if nice -n -1 true 2>/dev/null; then + NICE_HIGH="nice -n -10" + NICE_MED="nice -n -7" + JACKD_RT="-P 75" +else + echo "Note: cannot set elevated process priority (CAP_SYS_NICE not available); running at default priority" + NICE_HIGH="" + NICE_MED="" + JACKD_RT="" +fi + +# Setup jack only for hardware-based audio capture +if [ "$NODE_TYPE" != "dev-virt-s3" ]; then + echo "Starting Jack audio server for hardware audio capture..." + JACK_NO_AUDIO_RESERVATION=1 jackd -t 2000 $JACKD_RT -d alsa -d hw:$AUDIO_HW_ID -r $SAMPLE_RATE -p 1024 -n 10 -s & +fi #### Generate stream segments and manifests, and/or lossless archive @@ -46,7 +65,7 @@ echo "Node started at $timestamp" echo "Node is named $NODE_NAME and is of type $NODE_TYPE" ## NODE_TYPE set in .env filt to one of: "research"; "debug" (DASH-only); "hls-only"; or default (FLAC+HLS+DASH) -if [ $NODE_TYPE = "research" ]; then +if [ "$NODE_TYPE" = "research" ]; then #SAMPLE_RATE=192000 ## Setup Jack Audio outside for now # sudo echo @audio - memlock 256000 >> /etc/security/limits.conf @@ -55,52 +74,55 @@ if [ $NODE_TYPE = "research" ]; then echo "Sampling $CHANNELS channels from $AUDIO_HW_ID at $SAMPLE_RATE Hz with bitrate of 32 bits/sample..." echo "Asking ffmpeg to write $FLAC_DURATION second $SAMPLE_RATE Hz FLAC files..." ## Streaming HLS with FLAC archive - nice -n -10 ffmpeg -f jack -i ffjack \ + $NICE_HIGH ffmpeg -f jack -i ffjack \ -f segment -segment_time "00:00:$FLAC_DURATION.00" -strftime 1 "/tmp/$NODE_NAME/flac/%Y-%m-%d_%H-%M-%S_$NODE_NAME-$SAMPLE_RATE-$CHANNELS.flac" \ -f segment -segment_list "/tmp/$NODE_NAME/hls/$timestamp/live.m3u8" -segment_list_flags +live -segment_time $SEGMENT_DURATION -segment_format \ mpegts -ar $STREAM_RATE -ac 2 -acodec aac "/tmp/$NODE_NAME/hls/$timestamp/live%03d.ts" >/dev/null 2>/dev/null & -elif [ $NODE_TYPE = "debug" ]; then +elif [ "$NODE_TYPE" = "debug" ]; then echo "Sampling $CHANNELS channels from $AUDIO_HW_ID at $SAMPLE_RATE Hz with bitrate of 32 bits/sample..." echo "Asking ffmpeg to stream DASH via mpegts at $STREAM_RATE Hz..." ## Streaming DASH only via mpegts - nice -n -10 ffmpeg -t 0 -f jack -i ffjack -f mpegts udp://127.0.0.1:1234 & + $NICE_HIGH ffmpeg -t 0 -f jack -i ffjack -f mpegts udp://127.0.0.1:1234 & #### Stream with test engine live tools ## May need to adjust segment length in config_audio.json to match $SEGMENT_DURATION... - nice -n -7 ./test-engine-live-tools/bin/live-stream -c ./config_audio.json udp://127.0.0.1:1234 & -elif [ $NODE_TYPE = "hls-only" ]; then + $NICE_MED ./test-engine-live-tools/bin/live-stream -c ./config_audio.json udp://127.0.0.1:1234 & +elif [ "$NODE_TYPE" = "hls-only" ]; then echo "Sampling $CHANNELS channels from $AUDIO_HW_ID at $SAMPLE_RATE Hz..." echo "Asking ffmpeg to stream only HLS segments at $STREAM_RATE Hz......" ## Streaming HLS only via mpegts - nice -n -10 ffmpeg -f jack -i ffjack -f segment -segment_list "/tmp/$NODE_NAME/hls/$timestamp/live.m3u8" -segment_list_flags +live -segment_time $SEGMENT_DURATION -segment_format mpegts -ar $STREAM_RATE -ac $CHANNELS -threads 3 -acodec aac "/tmp/$NODE_NAME/hls/$timestamp/live%03d.ts" & -elif [ $NODE_TYPE = "dev-virt-s3" ]; then + $NICE_HIGH ffmpeg -f jack -i ffjack -f segment -segment_list "/tmp/$NODE_NAME/hls/$timestamp/live.m3u8" -segment_list_flags +live -segment_time $SEGMENT_DURATION -segment_format mpegts -ar $STREAM_RATE -ac $CHANNELS -threads 3 -acodec aac "/tmp/$NODE_NAME/hls/$timestamp/live%03d.ts" & +elif [ "$NODE_TYPE" = "dev-virt-s3" ]; then SAMPLE_RATE=48000 STREAM_RATE=48000 - echo "Sampling from $AUDIO_HW_ID at $SAMPLE_RATE Hz..." + echo "Sampling from virtual WAV file at $SAMPLE_RATE Hz..." echo "Asking ffmpeg to stream only HLS segments at $STREAM_RATE Hz......" ## Streaming HLS only via mpegts - nice -n -10 ffmpeg -re -fflags +genpts -stream_loop -1 -i "samples/haro-strait_2005.wav" \ + $NICE_HIGH ffmpeg -re -fflags +genpts -stream_loop -1 -i "samples/haro-strait_2005.wav" \ -f segment -segment_list "/tmp/$NODE_NAME/hls/$timestamp/live.m3u8" -segment_list_flags +live -segment_time $SEGMENT_DURATION -segment_format mpegts \ -ar $STREAM_RATE -ac $CHANNELS -threads 3 -acodec aac "/tmp/$NODE_NAME/hls/$timestamp/live%03d.ts" & else echo "unsupported please pick hls-only, research, or dev-virt-s3" fi -# takes a second for ffmpeg to make ffjack connection before we can connect -sleep 3 -jack_connect system:capture_1 ffjack:input_1 -jack_connect system:capture_2 ffjack:input_2 - -if [ $NODE_LOOPBACK = "true" ]; then - jack_connect system:capture_1 system:playback_1 - jack_connect system:capture_2 system:playback_2 +# Connect jack audio routing only for hardware-based modes +if [ "$NODE_TYPE" != "dev-virt-s3" ]; then + # takes a second for ffmpeg to make ffjack connection before we can connect + sleep 3 + jack_connect system:capture_1 ffjack:input_1 + jack_connect system:capture_2 ffjack:input_2 + + if [ "$NODE_LOOPBACK" = "true" ]; then + jack_connect system:capture_1 system:playback_1 + jack_connect system:capture_2 system:playback_2 + fi fi -if [ $NODE_LOOPBACK = "hls" ]; then +if [ "$NODE_LOOPBACK" = "hls" ]; then sleep 20 ffplay -nodisp /tmp/$NODE_NAME/hls/$timestamp/live.m3u8 fi -if [ $NODE_TYPE = "research" ]; then +if [ "$NODE_TYPE" = "research" ]; then python3 upload_s3.py & python3 upload_flac_s3.py else