Compare commits

...

125 Commits

Author SHA1 Message Date
Michel Promonet e9322f114d
Update README.md 1 month ago
Michel Promonet 2198200366
Create dependabot.yml 2 months ago
Michel Promonet 636d57edb6
Update anchore-syft.yml 2 months ago
Michel Promonet acae85b2af propagate HAVE_ALSA flag 2 months ago
mpromonet ce808915ed fix memory leak 2 months ago
Michel Promonet ef86de3922 update docker build 2 months ago
Michel Promonet 890cdbe5ae fix #331: remove submodule hls.js 2 months ago
Michel Promonet 44ea524f72
Update README.md 2 months ago
Michel Promonet 604fb52ade cirrusci: update remote docker 2 months ago
Michel Promonet 7b4909d98f update base image 2 months ago
Michel Promonet 21796e146d fix link 3 months ago
Michel Promonet eafa8d6931 add alsa in dependency 3 months ago
Michel Promonet 5a938d8e51 fix patch path when include in another project 3 months ago
Michel Promonet 2ac76dbc08
Update README.md 3 months ago
Michel Promonet 80ddc6c4ea
Update README.md 3 months ago
Michel Promonet 95caddff15
Create anchore-syft.yml 3 months ago
Michel Promonet aff10d0819
Rename UNLICENSE to Unlicense.txt 3 months ago
Michel Promonet 031dae8d5d try to fix scorecard 3 months ago
Michel Promonet 9e6a6af309
Create SECURITY.md 3 months ago
Michel Promonet 42c1881a61 add patch command 3 months ago
Michel Promonet 63ccf0df52 include live555 patch 3 months ago
Michel Promonet d390f12567 update logs 3 months ago
Michel Promonet 7a04a01631
Update snapcraft.yaml 3 months ago
Michel Promonet 508a3de19f
Update snapcraft.yaml 3 months ago
Michel Promonet 5cf22b4716 add multicast api 4 months ago
Michel Promonet 7f91dbe3fb
Update UNLICENSE 4 months ago
Michel Promonet be53549c4b
Update trivy.yml 4 months ago
Michel Promonet e2ad168d6c move multicast url decoding 4 months ago
Michel Promonet 0645e3cfe8 use default configuration for threads 5 months ago
Michel Promonet 14d25e42f1 update dep 5 months ago
Michel Promonet d4ae60fdf7
Update main.cpp 6 months ago
Michel Promonet 3320b9173a
Update main.cpp 6 months ago
Michel Promonet b12cf350dc
Update cross.yml 7 months ago
Michel Promonet bfdd34d45b add isKeyframe method 7 months ago
Michel Promonet 0b4247c538
Update snapcraft.yaml 11 months ago
Michel Promonet f729b00645
Update snapcraft.yaml 11 months ago
Michel Promonet 202c1fa9a2
Update snapcraft.yaml 11 months ago
Michel Promonet 3ab0401f8e
Update snapcraft.yaml 11 months ago
Michel Promonet 462cf630bd upgrade snap core 11 months ago
Michel Promonet 3f5a1f1a39 upgrade snap core 11 months ago
Michel Promonet e6cb11d4b9 add numClientSessions 11 months ago
Michel Promonet 8d75e1743e
Rename LICENSE to UNLICENSE 1 year ago
Michel Promonet 77d2ca07d9 fix ssl dep 1 year ago
Michel Promonet 5d10ef4729 use NO_STD_LIB 1 year ago
Michel Promonet 8c89c6e12e upgrade base image 1 year ago
Michel Promonet 1ee9496eff fix #316: use c++20 1 year ago
Michel Promonet 75c0088925
Update ort.yml 1 year ago
Michel Promonet f145b7a53b
Create ort.yml 1 year ago
Michel Promonet adec94cc86
Delete ort.yml 1 year ago
Michel Promonet 43b1cb39e9
Update ort.yml 1 year ago
Michel Promonet 789c47ab5a
Create ort.yml 1 year ago
Michel Promonet 646387575a
Create scorecard.yml 1 year ago
Michel Promonet 07e7201d5a export compile options 1 year ago
mpromonet 7dee1b863c int instead of long 1 year ago
mpromonet 0ecdc522ea add debug log for dqt parsing 1 year ago
Michel Promonet 3b5bb10b5d
Merge pull request #302 from fujiishigeki/mjpeg_dqt
Update handling of Quantization Table header for Motion JPEG
1 year ago
Shigeki Fujii bff93b99e3 Update handling of Quantization Table header for Motion JPEG 1 year ago
Michel Promonet 59a242eda4
Merge pull request #301 from azsde/retry-upon-camera-unavailable
[BUGFIX #299] Do not exit thread upon EAGAIN error when fetching frames
1 year ago
Azsde cb9a64b395 [BUGFIX #299] Do not exit thread upon EAGAIN error when fetching frames 1 year ago
Michel Promonet 9029be33c5
Update docker.yml 2 years ago
Michel Promonet 3e14254a5c
Update ccpp.yml 2 years ago
Michel Promonet 6d37dce4ef
Update docker.yml 2 years ago
Michel Promonet f409ac3a1c
Update config.yml 2 years ago
Michel Promonet 6b7e20131d
Update .cirrus.yml 2 years ago
Michel Promonet 9af7e4ff34
Merge pull request #296 from s-fairchild/arm64-docker-alsa
Update arm64 Dockerfile to build audio support
2 years ago
s-fairchild 4767fb1cc8 Update arm64 Dockerfile to build audio support
I'm not sure if there's a reason audio support wasn't being built in the arm64 images, I would like to update the arm64 Dockerfile to support alsa.
2 years ago
mpromonet c0f06018ae improve computation of arch for deb package 2 years ago
mpromonet a80b4cf202 add interface to get audio format list 2 years ago
mpromonet a38262b88e add method to convert audio fmt to string 2 years ago
mpromonet dc3c7d8802 update libv4l2cpp 2 years ago
mpromonet 2a360ab574 fix init frames 2 years ago
mpromonet eaeda29aec Merge branch 'master' of https://github.com/mpromonet/v4l2rtspserver into HEAD 2 years ago
mpromonet 0165defff3 add api to getInitFrames 2 years ago
mpromonet cdbc3a6b85 Merge branch 'master' of github.com:mpromonet/v4l2rtspserver 2 years ago
mpromonet 70ce4d562b fix #291 2 years ago
mpromonet a5c0762671 allow to disable ssl 2 years ago
mpromonet e92e7b41f6 Merge branch 'master' of https://github.com/mpromonet/v4l2rtspserver into HEAD 2 years ago
mpromonet 9d725ecf6c embeded libv4l2cpp 2 years ago
mpromonet 081a550130 allow to ignore try to link with staticcpp 2 years ago
Michel Promonet 30a0531776
Update CMakeLists.txt 2 years ago
mpromonet 631f43cdcf for #290: allow default value in -M option 2 years ago
Michel Promonet 233b631fc4
Update snapcraft.yaml 2 years ago
mpromonet 7628fa4711 Merge branch 'master' of https://github.com/mpromonet/v3l2rtspserver into HEAD 2 years ago
mpromonet 59cfcf6f32 move audio fonctions 2 years ago
Michel Promonet 53b7d9173e
Update config.yml 2 years ago
Michel Promonet 70bb3a4b33
Update config.yml 2 years ago
mpromonet 83fbac1f3e add nv12 format to default 2 years ago
mpromonet 737c4f88d9 more tries with raw 2 years ago
mpromonet 4c140e29d5 allow to remove a session 2 years ago
mpromonet b03e11fd04 Merge branch 'master' of https://github.com/mpromonet/v4l2rtspserver into HEAD 2 years ago
mpromonet 4542647b72 add api to get url 2 years ago
mpromonet 3f81e89799 rename submodule v4l2wrapper according to git repo name 2 years ago
mpromonet b9b48c40b8 update libv4l2cpp 2 years ago
mpromonet 6209874075 upgrade libv4l2cpp 2 years ago
Michel Promonet c7a167ca5e
Update config.yml 2 years ago
mpromonet b007df2faa Merge branch 'master' of github.com:mpromonet/v4l2rtspserver 2 years ago
mpromonet ed63eb4820 for #277: disable output when multiple capture device are used 2 years ago
Michel Promonet de3c5c589b
Update codeql-analysis.yml 2 years ago
Michel Promonet e19f7706bc
Update trivy.yml 2 years ago
mpromonet a43e094b2e update circleci replacing machine with docker 2 years ago
mpromonet 03022b84c7 missing pragma once 2 years ago
mpromonet 0c63d58d1b Merge branch 'master' of github.com:mpromonet/v4l2rtspserver 2 years ago
mpromonet 90638d126e static link stdc++ if possible 2 years ago
Michel Promonet 930b3996cb
Update CMakeLists.txt 2 years ago
Michel Promonet 311843197c
Update CMakeLists.txt 2 years ago
mpromonet ce176b79ab Merge branch 'master' of github.com:mpromonet/v4l2rtspserver 2 years ago
mpromonet 43ec7efd2c publish artefact to release 2 years ago
mpromonet 84fa7fd46d Merge branch 'master' of https://github.com/mpromonet/v4l2rtspserver 2 years ago
mpromonet 0f95b1b539 set cpack architehture 2 years ago
mpromonet d8edd42c64 fix #279: allow to use static live 2 years ago
mpromonet 917ee3ecad factorize cross compilation actions 2 years ago
mpromonet 823aa452ec factorize cross compilation actions 2 years ago
mpromonet 906d8dcd0e factorize cross compilation actions 2 years ago
mpromonet 00470ad936 factorize cross compilation actions 2 years ago
mpromonet f10d62512f try to add more build 2 years ago
mpromonet 5e56bb1b89 update package name adding buildtype 2 years ago
mpromonet 1493320699 update cross action 2 years ago
mpromonet 3ea361dff3 add more github actions 2 years ago
mpromonet 14b1001608 add more github actions 2 years ago
mpromonet c6300bcea5 add more github actions 2 years ago
mpromonet 93dfec6764 add more github actions 2 years ago
mpromonet d4b9e7b916 update artifact name 2 years ago
mpromonet 0251671709 fix #278: initialize m_format earlier 2 years ago
mpromonet 34dd957bd1 fix build with old live555 release 2 years ago
Michel Promonet 0fd4cb9f6b
Update README.md 2 years ago

@ -1,16 +1,17 @@
defaults: &defaults
working_directory: /v4l2rtspserver
docker:
- image: heroku/heroku:18
- image: cimg/base:2022.09
user: root
version: 2
version: 2.1
jobs:
build_x86_64:
<<: *defaults
steps:
- checkout
- run: apt-get update; apt-get install -y --no-install-recommends cmake autoconf automake libtool git make g++ liblog4cpp5-dev libasound2-dev pkg-config
- run: apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cmake autoconf automake libtool git make g++ liblog4cpp5-dev libasound2-dev pkg-config
- run: cmake . && make
- run: cpack .
- run: mkdir -p /distrib && cp *.deb /distrib
@ -28,7 +29,7 @@ jobs:
steps:
- checkout
- run: apt-get update; apt-get install -y --no-install-recommends cmake autoconf automake libtool git make pkg-config
- run: apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cmake autoconf automake libtool git make pkg-config; apt remove -y libssl-dev
- run:
command: wget -qO- ${CROSSCOMPILER} | tar xz -C /opt
no_output_timeout: 30m
@ -50,7 +51,7 @@ jobs:
steps:
- checkout
- run: apt-get update; apt-get install -y --no-install-recommends cmake autoconf automake libtool git make pkg-config
- run: apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cmake autoconf automake libtool git make pkg-config; apt remove -y libssl-dev
- run:
command: wget -qO- ${CROSSCOMPILER} | tar xz -C /opt
no_output_timeout: 30m
@ -68,7 +69,7 @@ jobs:
steps:
- checkout
- run: apt-get update; apt-get install -y --no-install-recommends cmake autoconf automake libtool git make g++-mips-linux-gnu pkg-config
- run: apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cmake autoconf automake libtool git make g++-mips-linux-gnu pkg-config; apt remove -y libssl-dev
- run: cmake -DCMAKE_TOOLCHAIN_FILE=${CIRCLE_WORKING_DIRECTORY}/mips.toolchain . && make
- run: cpack .
- run: mkdir -p /distrib && cp *.deb /distrib
@ -82,7 +83,7 @@ jobs:
steps:
- checkout
- run: apt-get update; apt-get install -y --no-install-recommends cmake autoconf automake libtool git make gcc-aarch64-linux-gnu g++-aarch64-linux-gnu pkg-config
- run: apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends cmake autoconf automake libtool git make gcc-aarch64-linux-gnu g++-aarch64-linux-gnu pkg-config; apt remove -y libssl-dev
- run: cmake -DCMAKE_SYSTEM_PROCESSOR=arm64 -DCPACK_DEBIAN_PACKAGE_ARCHITECTURE=arm64 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY . && make
- run: cpack .
- run: mkdir -p /distrib && cp *.deb /distrib
@ -106,8 +107,8 @@ jobs:
steps:
- attach_workspace:
at: /
- run: apt-get update; apt-get install -y --no-install-recommends golang
- run: go get github.com/tcnksm/ghr
- run: apt-get update; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends golang
- run: go install github.com/tcnksm/ghr@latest
- deploy:
name: "Deploy to Github"
command: $(go env GOPATH)/bin/ghr -t $GITHUB_TOKEN -u $CIRCLE_PROJECT_USERNAME -r $CIRCLE_PROJECT_REPONAME $CIRCLE_TAG /distrib
@ -116,9 +117,10 @@ jobs:
# publish docker amd64
# -------------------------------------
publish_docker_linuxamd64:
machine:
docker_layer_caching: false
<<: *defaults
steps:
- setup_remote_docker
- checkout
- run: git submodule update --init
- run:
@ -132,57 +134,64 @@ jobs:
# publish docker arm
# -------------------------------------
publish_docker_linuxarm32v7:
machine:
docker_layer_caching: false
<<: *defaults
steps:
- setup_remote_docker
- checkout
- run: git submodule update --init
- run:
command: |
export TAG=${CIRCLE_TAG:-latest}
docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
docker build --pull -t $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm32v7 -f Dockerfile.rpi .
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build --pull -t $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm32v7 --build-arg IMAGE=balenalib/raspberry-pi2 .
docker push $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm32v7
# -------------------------------------
# publish docker arm
# -------------------------------------
publish_docker_linuxarm32v6:
machine:
docker_layer_caching: false
<<: *defaults
steps:
- setup_remote_docker
- checkout
- run: git submodule update --init
- run:
command: |
export TAG=${CIRCLE_TAG:-latest}
docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
docker build --build-arg ARCH=armv6l --pull -t $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm32v6 -f Dockerfile.rpi .
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build --build-arg ARCH=armv6l --pull -t $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm32v6 --build-arg IMAGE=balenalib/raspberry-pi .
docker push $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm32v6
# -------------------------------------
# publish docker arm
# -------------------------------------
publish_docker_linuxarm64:
machine:
docker_layer_caching: false
<<: *defaults
steps:
- setup_remote_docker
- checkout
- run: git submodule update --init
- run:
command: |
export TAG=${CIRCLE_TAG:-latest}
docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
docker build --pull -t $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm64 -f Dockerfile.arm64 .
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build --pull -t $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm64 --build-arg IMAGE=arm64v8/ubuntu:24.04 .
docker push $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:${TAG}-arm64
# -------------------------------------
# publish docker manifest
# -------------------------------------
publish_docker_multiarch:
machine:
image: circleci/classic:201808-01
<<: *defaults
steps:
- setup_remote_docker
- run:
command: |
mkdir $HOME/.docker

@ -1,11 +1,17 @@
linux_docker_builder:
script: docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-linux . -f Dockerfile
script: docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-linux .
linuxarmv7_docker_builder:
script: docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-rpi . -f Dockerfile.rpi
script: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-armv7 . --build-arg IMAGE=balenalib/raspberry-pi2
linuxarmv6_docker_builder:
script: docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-armv6 . -f Dockerfile.rpi --build-arg ARCH=armv6l
script: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-armv6 . --build-arg IMAGE=balenalib/raspberry-pi
linuxarm64_docker_builder:
script: docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-arm64 . -f Dockerfile.arm64
script: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build -t $CIRRUS_REPO_FULL_NAME:cirrus-arm64 . --build-arg IMAGE=arm64v8/ubuntu:24.04

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "docker"
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

@ -0,0 +1,38 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow checks out code, builds an image, performs a container image
# scan with Anchore's Syft tool, and uploads the results to the GitHub Dependency
# submission API.
# For more information on the Anchore sbom-action usage
# and parameters, see https://github.com/anchore/sbom-action. For more
# information about the Anchore SBOM tool, Syft, see
# https://github.com/anchore/syft
name: Anchore Syft SBOM scan
on:
push:
branches: [ "master" ]
permissions:
contents: write
jobs:
Anchore-Build-Scan:
permissions:
contents: write # required to upload to the Dependency submission API
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4
- name: Build the Docker image
run: docker build . --file Dockerfile --tag localbuild/testimage:latest
- name: Scan the image and upload dependency results
uses: anchore/sbom-action@v0
with:
image: "localbuild/testimage:latest"
artifact-name: image.spdx.json
dependency-snapshot: true

@ -4,19 +4,32 @@ on: [push]
jobs:
build:
strategy:
matrix:
buildtype: [Debug, Release]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: cmake
run: cmake .
- name: make
run: make
- name: build
run: |
cmake -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }} .
make
- name: cpack
run: cpack -B $(pwd)/artifact
run: |
cpack
echo "artifactPath=$(ls *.deb)" >> $GITHUB_ENV
- uses: actions/upload-artifact@v3
with:
name: ${{ env.artifactPath }}
path: ${{ env.artifactPath }}
- uses: actions/upload-artifact@v1
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
name: pkg
path: artifact
files: ${{ env.artifactPath }}

@ -29,26 +29,12 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages
# with:
# languages: go, javascript, csharp, python, cpp, java
uses: github/codeql-action/init@v2
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

@ -0,0 +1,74 @@
name: C/C++ CI linux cross compilation arm64/armhf/mips
on: [push]
jobs:
build:
strategy:
matrix:
include:
- buildtype: Debug
system: arm64
crosscompiler: aarch64-linux-gnu
- buildtype: Release
system: arm64
crosscompiler: aarch64-linux-gnu
- buildtype: Debug
system: armhf
crosscompiler: arm-linux-gnueabihf
- buildtype: Release
system: armhf
crosscompiler: arm-linux-gnueabihf
- buildtype: Debug
system: mips
crosscompiler: mips-linux-gnu
- buildtype: Release
system: mips
crosscompiler: mips-linux-gnu
- buildtype: Debug
system: mipsel
crosscompiler: mipsel-linux-gnu
- buildtype: Release
system: mipsel
crosscompiler: mipsel-linux-gnu
- buildtype: Debug
system: riscv64
crosscompiler: riscv64-linux-gnu
- buildtype: Release
system: riscv64
crosscompiler: riscv64-linux-gnu
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: pkg
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends ca-certificates xz-utils cmake make pkg-config git g++-${{ matrix.crosscompiler }}
- name: build
run: |
cmake -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }} -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.system }} -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=${{ matrix.crosscompiler }}-gcc -DCMAKE_CXX_COMPILER=${{ matrix.crosscompiler }}-g++ -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY -DCMAKE_FIND_ROOT_PATH=/usr/aarch64-linux-gnu .
make
- name: cpack
run: |
cpack
echo "artifactPath=$(ls *.deb)" >> $GITHUB_ENV
- uses: actions/upload-artifact@v3
with:
name: ${{ env.artifactPath }}
path: ${{ env.artifactPath }}
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: ${{ env.artifactPath }}

@ -0,0 +1,31 @@
name: C/C++ CI docker
on: [push]
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-latest
image: arm64v8/ubuntu:24.04
label: arm64
- os: ubuntu-latest
image: balenalib/raspberry-pi
label: armv6l
- os: ubuntu-latest
image: balenalib/raspberry-pi2
label: armv7
- os: ubuntu-latest
image: ubuntu:24.04
label: amd64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Build an image from Dockerfile
run: |
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
docker build -t docker.io/${{ github.repository }}:${{ matrix.label }} --build-arg IMAGE=${{ matrix.image }} .

@ -0,0 +1,14 @@
name: ORT
on: [push]
jobs:
ort:
runs-on: ubuntu-latest
steps:
- name: Use HTTPS instead of SSH for Git cloning
run: git config --global url.https://github.com/.insteadOf ssh://git@github.com/
- name: Checkout project
uses: actions/checkout@v3
- name: Run GitHub Action for ORT
uses: oss-review-toolkit/ort-ci-github-action@main

@ -0,0 +1,72 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '26 13 * * 0'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: false
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4
with:
sarif_file: results.sarif

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Build an image from Dockerfile
run: |
@ -26,6 +26,6 @@ jobs:
severity: 'CRITICAL,HIGH'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v1
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'

1
.gitignore vendored

@ -13,3 +13,4 @@ install_manifest.txt
*.deb
#live
live/
live555-latest.tar.gz

6
.gitmodules vendored

@ -1,6 +1,4 @@
[submodule "v4l2wrapper"]
path = v4l2wrapper
path = libv4l2cpp
url = https://github.com/mpromonet/v4l2wrapper
[submodule "hls.js"]
path = hls.js
url = https://github.com/video-dev/hls.js

@ -3,20 +3,26 @@ cmake_minimum_required(VERSION 3.0)
project(v4l2rtspserver)
option(COVERAGE "Coverage" OFF)
option (WITH_SSL "Enable SSL support" ON)
set(ALSA ON CACHE BOOL "use ALSA if available")
set(STATICSTDCPP ON CACHE BOOL "use gcc static lib if available")
set(LOG4CPP OFF CACHE BOOL "use log4cpp if available")
set(LIVE555URL http://www.live555.com/liveMedia/public/live555-latest.tar.gz CACHE STRING "live555 url")
set(LIVE555CFLAGS -DBSD=1 -DSOCKLEN_T=socklen_t -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 -DALLOW_RTSP_SERVER_PORT_REUSE=1 CACHE STRING "live555 CFGLAGS")
set(LIVE555CFLAGS -DBSD=1 -DSOCKLEN_T=socklen_t -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE=1 -DALLOW_RTSP_SERVER_PORT_REUSE=1 -DNO_STD_LIB=1 CACHE STRING "live555 CFGLAGS")
set(CMAKE_CXX_STANDARD 11)
if(NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE "Release")
endif()
set(CMAKE_CXX_STANDARD 20)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake/")
# set version based on git
find_package(Git)
if(GIT_FOUND)
EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty OUTPUT_VARIABLE VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
EXECUTE_PROCESS(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} describe --tags --always --dirty OUTPUT_VARIABLE VERSION OUTPUT_STRIP_TRAILING_WHITESPACE)
STRING(REGEX REPLACE "^v(.*)" "\\1" VERSION "${VERSION}")
add_definitions("-DVERSION=\"${VERSION}\"")
endif()
@ -38,8 +44,6 @@ message(STATUS "CMAKE_C_COMPILER=${CMAKE_C_COMPILER}")
message(STATUS "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}")
#pthread
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package (Threads)
target_link_libraries (${PROJECT_NAME} Threads::Threads)
@ -57,44 +61,52 @@ if (LOG4CPP)
endif ()
# openssl ?
find_package(OpenSSL QUIET)
if (WITH_SSL)
find_package(OpenSSL QUIET)
endif()
MESSAGE("OpenSSL_FOUND = ${OpenSSL_FOUND}")
# live555
find_package(LiveMedia)
find_package(UsageEnvironment)
find_package(BasicUsageEnvironment)
find_package(GroupSock)
find_package(libliveMedia)
find_package(libUsageEnvironment)
find_package(libBasicUsageEnvironment)
find_package(libgroupsock)
if (LIBLIVEMEDIA_FOUND AND LIBBASICUSAGEENVIRONMENT_FOUND AND LIBUSAGEENVIRONMENT_FOUND AND LIBGROUPSOCK_FOUND)
message(STATUS "live555 loaded")
set(LIVEINCLUDE ${LIBLIVEMEDIA_INCLUDE_DIR} ${LIBBASICUSAGEENVIRONMENT_INCLUDE_DIR} ${LIBUSAGEENVIRONMENT_INCLUDE_DIR} ${LIBGROUPSOCK_INCLUDE_DIR})
target_link_libraries (${PROJECT_NAME} ${LIBLIVEMEDIA_LIBRARY} ${LIBUSAGEENVIRONMENT_LIBRARY} ${LIBBASICUSAGEENVIRONMENT_LIBRARY} ${LIBGROUPSOCK_LIBRARY})
SET(LIVE_LIBRARIES ${LIBLIVEMEDIA_LIBRARY} ${LIBUSAGEENVIRONMENT_LIBRARY} ${LIBBASICUSAGEENVIRONMENT_LIBRARY} ${LIBGROUPSOCK_LIBRARY})
SET(CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_PACKAGE_DEPENDS}livemedia-utils,)
else()
set(LIVE ${CMAKE_BINARY_DIR}/live)
set(LIVEINCLUDE ${LIVE}/groupsock/include ${LIVE}/liveMedia/include ${LIVE}/UsageEnvironment/include ${LIVE}/BasicUsageEnvironment/include)
if (NOT EXISTS ${LIVE})
file (DOWNLOAD ${LIVE555URL} ${CMAKE_BINARY_DIR}/live555-latest.tar.gz )
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xvf ${CMAKE_BINARY_DIR}/live555-latest.tar.gz RESULT_VARIABLE unpack_result)
message(STATUS "extract live555")
EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E tar xf ${CMAKE_BINARY_DIR}/live555-latest.tar.gz RESULT_VARIABLE unpack_result)
if(NOT unpack_result STREQUAL "0")
message(FATAL_ERROR "Fetching and compiling live555 failed!")
endif()
message(STATUS "patch live555")
EXECUTE_PROCESS(COMMAND patch -d ${CMAKE_BINARY_DIR} -f -p 1 -i ${PROJECT_SOURCE_DIR}/patches/001_live555_sprintf_format RESULT_VARIABLE patch_result)
if(NOT patch_result STREQUAL "0")
message(FATAL_ERROR "Patching live555 failed! error:${patch_result}")
endif()
endif()
FILE(GLOB LIVESOURCE ${LIVE}/groupsock/*.c* ${LIVE}/liveMedia/*.c* ${LIVE}/UsageEnvironment/*.c* ${LIVE}/BasicUsageEnvironment/*.c*)
if (NOT OpenSSL_FOUND)
set(LIVE555CFLAGS ${LIVE555CFLAGS} -DNO_OPENSSL=1)
STRING(REGEX REPLACE "${LIVE}/liveMedia/HMAC_SHA1.cpp" "" LIVESOURCE "${LIVESOURCE}")
endif()
add_definitions(${LIVE555CFLAGS})
endif()
# librtsp
aux_source_directory(src LIBV4L2RTSP_SRC_FILES)
add_library (libv4l2rtspserver STATIC ${LIVESOURCE} ${LIBV4L2RTSP_SRC_FILES})
target_include_directories(libv4l2rtspserver PUBLIC inc ${LIVEINCLUDE})
target_link_libraries (${PROJECT_NAME} libv4l2rtspserver)
target_compile_definitions(libv4l2rtspserver PUBLIC ${LIVE555CFLAGS})
target_link_libraries (${PROJECT_NAME} libv4l2rtspserver ${LIVE_LIBRARIES})
set (LIBRARIES "")
if (OpenSSL_FOUND)
target_link_libraries(${PROJECT_NAME} OpenSSL::SSL)
set(LIBRARIES ${LIBRARIES} ${OPENSSL_LIBRARIES})
endif ()
#ALSA
@ -102,18 +114,31 @@ if (ALSA)
find_package(ALSA QUIET)
MESSAGE("ALSA_FOUND = ${ALSA_FOUND}")
if (ALSA_LIBRARY)
add_definitions(-DHAVE_ALSA)
target_link_libraries (${PROJECT_NAME} ${ALSA_LIBRARY})
target_compile_definitions(libv4l2rtspserver PUBLIC HAVE_ALSA)
set(LIBRARIES ${LIBRARIES} ${ALSA_LIBRARY})
SET(CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_PACKAGE_DEPENDS}libasound2,)
endif ()
endif()
# v4l2wrapper
# libv4l2cpp
EXEC_PROGRAM("git submodule update --init")
add_subdirectory(v4l2wrapper)
include_directories("v4l2wrapper/inc")
target_link_libraries (${PROJECT_NAME} v4l2wrapper)
add_subdirectory(libv4l2cpp EXCLUDE_FROM_ALL)
target_include_directories(libv4l2rtspserver PUBLIC libv4l2cpp/inc)
target_link_libraries (libv4l2rtspserver PUBLIC libv4l2cpp ${LIBRARIES})
# static link of stdc++ if available
if (STATICSTDCPP)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-static-libgcc" CXX_SUPPORTS_STATIC_LIBGCC)
if (CXX_SUPPORTS_STATIC_LIBGCC)
target_link_libraries (${PROJECT_NAME} -static-libgcc)
endif()
CHECK_CXX_COMPILER_FLAG("-static-libstdc++" CXX_SUPPORTS_STATIC_LIBSTDCPP)
if (CXX_SUPPORTS_STATIC_LIBSTDCPP)
target_link_libraries (${PROJECT_NAME} -static-libstdc++)
endif()
endif()
#testing
enable_testing()
@ -135,12 +160,20 @@ endif (SYSTEMD_FOUND)
# package
install (TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)
install (FILES index.html DESTINATION share/${PROJECT_NAME}/)
install (FILES hls.js/dist/hls.light.min.js DESTINATION share/${PROJECT_NAME}/hls.js/dist/)
install (FILES hls.js DESTINATION share/${PROJECT_NAME}/)
SET(CPACK_GENERATOR "DEB")
IF(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE amd64)
elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "armv[6-7].*")
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE armhf)
elseif(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
endif()
message(STATUS "CPACK_DEBIAN_PACKAGE_ARCHITECTURE=${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Michel Promonet")
SET(CPACK_PACKAGE_CONTACT "michel.promonet@free.fr")
SET(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR})
SET(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_BUILD_TYPE})
STRING(REGEX REPLACE ",$" "" CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}")
SET(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
if(VERSION)

@ -1,19 +1,22 @@
FROM ubuntu:20.04 as builder
ARG IMAGE=ubuntu:24.04
FROM $IMAGE as builder
LABEL maintainer michel.promonet@free.fr
WORKDIR /v4l2rtspserver
COPY . /v4l2rtspserver
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates g++ autoconf automake libtool xz-utils cmake make pkg-config git wget libasound2-dev libssl-dev \
&& cmake . && make install && apt-get clean && rm -rf /var/lib/apt/lists/
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates g++ autoconf automake libtool xz-utils cmake make patch pkg-config git wget libasound2-dev libssl-dev
COPY . .
FROM ubuntu:20.04
RUN cmake . && make install && apt-get clean && rm -rf /var/lib/apt/lists/
FROM $IMAGE
WORKDIR /usr/local/share/v4l2rtspserver
COPY --from=builder /usr/local/bin/ /usr/local/bin/
COPY --from=builder /usr/local/share/v4l2rtspserver/ /usr/local/share/v4l2rtspserver/
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates libasound2 libssl1.1 && apt-get clean && rm -rf /var/lib/apt/lists/
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates libasound2-dev libssl-dev && apt-get clean && rm -rf /var/lib/apt/lists/
COPY --from=builder /usr/local/bin/ /usr/local/bin/
COPY --from=builder /usr/local/share/v4l2rtspserver/ /usr/local/share/v4l2rtspserver/
ENTRYPOINT [ "/usr/local/bin/v4l2rtspserver" ]
CMD [ "-S" ]

@ -1,22 +0,0 @@
FROM ubuntu:20.04 as builder
LABEL maintainer michel.promonet@free.fr
WORKDIR /v4l2rtspserver
COPY . /v4l2rtspserver
ARG ARCH=arm64
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates xz-utils cmake make pkg-config git wget gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
&& cmake -DCMAKE_SYSTEM_PROCESSOR=${ARCH} -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY . \
&& make install \
&& apt-get clean && rm -rf /var/lib/apt/lists/
FROM arm64v8/ubuntu:20.04
WORKDIR /usr/local/share/v4l2rtspserver
COPY --from=builder /usr/local/bin/ /usr/local/bin/
COPY --from=builder /usr/local/share/v4l2rtspserver/ /usr/local/share/v4l2rtspserver/
ENTRYPOINT [ "/usr/local/bin/v4l2rtspserver" ]
CMD [ "-S" ]

@ -1,24 +0,0 @@
FROM debian as builder
LABEL maintainer michel.promonet@free.fr
WORKDIR /v4l2rtspserver
COPY . /v4l2rtspserver
ARG ARCH=armv7l
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates xz-utils cmake make pkg-config git wget \
&& git clone --depth 1 https://github.com/raspberrypi/tools.git /rpi_tools \
&& export PATH=/rpi_tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin:$PATH \
&& cmake -DCMAKE_SYSTEM_PROCESSOR=${ARCH} -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY . \
&& make install \
&& apt-get clean && rm -rf /var/lib/apt/lists/
FROM balenalib/raspberry-pi
WORKDIR /usr/local/share/v4l2rtspserver
COPY --from=builder /usr/local/bin/ /usr/local/bin/
COPY --from=builder /usr/local/share/v4l2rtspserver/ /usr/local/share/v4l2rtspserver/
ENTRYPOINT [ "/usr/local/bin/v4l2rtspserver" ]
CMD [ "-S" ]

@ -1,16 +1,17 @@
[![TravisCI](https://travis-ci.org/mpromonet/v4l2rtspserver.png)](https://travis-ci.org/mpromonet/v4l2rtspserver)
[![CircleCI](https://circleci.com/gh/mpromonet/v4l2rtspserver.svg?style=shield)](https://circleci.com/gh/mpromonet/v4l2rtspserver)
[![CirusCI](https://api.cirrus-ci.com/github/mpromonet/v4l2rtspserver.svg?branch=master)](https://cirrus-ci.com/github/mpromonet/v4l2rtspserver)
[![Snap Status](https://snapcraft.io//v4l2-rtspserver/badge.svg)](https://snapcraft.io/v4l2-rtspserver)
[![GithubCI](https://github.com/mpromonet/v4l2rtspserver/workflows/C/C++%20CI/badge.svg)](https://github.com/mpromonet/v4l2rtspserver/actions)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/aa0c28514aa843ea9fa7da358d905871)](https://www.codacy.com/app/michelpromonet_2643/v4l2rtspserver?utm_source=github.com&utm_medium=referral&utm_content=mpromonet/v4l2rtspserver&utm_campaign=badger)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/4602f447d1c1408d865ebb4ef68f12f1)](https://app.codacy.com/gh/mpromonet/v4l2rtspserver/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/4644/badge.svg)](https://scan.coverity.com/projects/4644)
[![Coverage Status](https://coveralls.io/repos/github/mpromonet/v4l2rtspserver/badge.svg?branch=master)](https://coveralls.io/github/mpromonet/v4l2rtspserver?branch=master)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8853/badge)](https://www.bestpractices.dev/projects/8853)
[![Release](https://img.shields.io/github/release/mpromonet/v4l2rtspserver.svg)](https://github.com/mpromonet/v4l2rtspserver/releases/latest)
[![Download](https://img.shields.io/github/downloads/mpromonet/v4l2rtspserver/total.svg)](https://github.com/mpromonet/v4l2rtspserver/releases/latest)
[![Docker Pulls](https://img.shields.io/docker/pulls/mpromonet/v4l2rtspserver.svg)](https://hub.docker.com/r/mpromonet/v4l2rtspserver/)
[![Openwrt](https://img.shields.io/badge/OpenWRT-00B5E2?logo=OpenWrt)](https://openwrt.org/packages/pkgdata/v4l2rtspserver)
v4l2rtspserver
====================
@ -60,7 +61,8 @@ Usage
-c : don't repeat config (default repeat config before IDR frame)
-t secs : RTCP expiration timeout (default 65)
-S[secs] : HTTP segment duration (enable HLS & MPEG-DASH)
-x <sslkeycert> : enable RTSPS & SRTP
V4L2 options :
-r : V4L2 capture using read interface (default use memory mapped buffers)
-w : V4L2 capture using write interface (default use memory mapped buffers)
@ -122,9 +124,6 @@ This RTSP server works with Raspberry Pi camera using :
sudo modprobe -v bcm2835-v4l2
- the closed source V4L2 driver for the Raspberry Pi Camera Module http://www.linux-projects.org/uv4l/
sudo uv4l --driver raspicam --auto-video_nr --encoding h264
Using v4l2loopback
-----------------------
@ -156,7 +155,7 @@ It is now possible to play HLS url directly from browser :
* using Firefox installing [Native HLS addons](https://addons.mozilla.org/en-US/firefox/addon/native_hls_playback)
* using Chrome installing [Native HLS playback](https://chrome.google.com/webstore/detail/native-hls-playback/emnphkkblegpebimobpbekeedfgemhof)
There is also a small HTML page that use hls.js and dash.js, but dash still not work because player doesnot support MP2T format.
There is also a small HTML page that use hls.js.
Using Docker image
===============

@ -0,0 +1,21 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

@ -21,4 +21,4 @@ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
For more information, please refer to <https://unlicense.org>

@ -1 +0,0 @@
Subproject commit e90a1b999071e88af0f734ef18b9e8520c839cec

File diff suppressed because one or more lines are too long

@ -46,13 +46,15 @@ class ALSACapture : public DeviceInterface
int configureFormat(snd_pcm_hw_params_t *hw_params);
public:
virtual size_t read(char* buffer, size_t bufferSize);
virtual int getFd();
virtual unsigned long getBufferSize() { return m_bufferSize; }
virtual size_t read(char* buffer, size_t bufferSize);
virtual int getFd();
virtual unsigned long getBufferSize() { return m_bufferSize; }
virtual unsigned long getSampleRate() { return m_params.m_sampleRate; }
virtual unsigned long getChannels () { return m_params.m_channels; }
virtual int getAudioFormat () { return m_fmt; }
virtual int getSampleRate() { return m_params.m_sampleRate; }
virtual int getChannels () { return m_params.m_channels; }
virtual int getAudioFormat () { return m_fmt; }
virtual std::list<int> getAudioFormatList() { return m_fmtList; }
private:
snd_pcm_t* m_pcm;
@ -60,6 +62,7 @@ class ALSACapture : public DeviceInterface
unsigned long m_periodSize;
ALSACaptureParameters m_params;
snd_pcm_format_t m_fmt;
std::list<int> m_fmtList;
};

@ -46,7 +46,7 @@ class BaseServerMediaSubsession
} else {
m_format = BaseServerMediaSubsession::getAudioRtpFormat(device->getAudioFormat(), device->getSampleRate(), device->getChannels());
}
LOG(NOTICE) << "format:" << m_format;
LOG(NOTICE) << "RTP format:" << m_format;
}
}
@ -58,14 +58,19 @@ class BaseServerMediaSubsession
std::string rtpFormat;
switch(format)
{
case V4L2_PIX_FMT_HEVC : rtpFormat = "video/H265"; break;
case V4L2_PIX_FMT_H264 : rtpFormat = "video/H264"; break;
case V4L2_PIX_FMT_MJPEG: rtpFormat = "video/JPEG"; break;
case V4L2_PIX_FMT_JPEG : rtpFormat = "video/JPEG"; break;
case V4L2_PIX_FMT_VP8 : rtpFormat = "video/VP8" ; break;
case V4L2_PIX_FMT_VP9 : rtpFormat = "video/VP9" ; break;
case V4L2_PIX_FMT_YUYV : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_UYVY : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_HEVC : rtpFormat = "video/H265"; break;
case V4L2_PIX_FMT_H264 : rtpFormat = "video/H264"; break;
case V4L2_PIX_FMT_MJPEG : rtpFormat = "video/JPEG"; break;
case V4L2_PIX_FMT_JPEG : rtpFormat = "video/JPEG"; break;
case V4L2_PIX_FMT_VP8 : rtpFormat = "video/VP8" ; break;
case V4L2_PIX_FMT_VP9 : rtpFormat = "video/VP9" ; break;
case V4L2_PIX_FMT_YUV444: rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_UYVY : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_NV12 : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_BGR24 : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_BGR32 : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_RGB24 : rtpFormat = "video/RAW" ; break;
case V4L2_PIX_FMT_RGB32 : rtpFormat = "video/RAW" ; break;
}
return rtpFormat;

@ -9,7 +9,7 @@
#pragma once
#include <list>
// ---------------------------------
// Device Interface
@ -19,14 +19,16 @@ class DeviceInterface
public:
virtual size_t read(char* buffer, size_t bufferSize) = 0;
virtual int getFd() = 0;
virtual unsigned long getBufferSize() = 0;
virtual int getWidth() { return -1; }
virtual int getHeight() { return -1; }
virtual int getVideoFormat() { return -1; }
virtual unsigned long getSampleRate() { return -1; }
virtual unsigned long getChannels() { return -1; }
virtual int getAudioFormat() { return -1; }
virtual ~DeviceInterface() {};
virtual unsigned long getBufferSize() = 0;
virtual int getWidth() { return -1; }
virtual int getHeight() { return -1; }
virtual int getVideoFormat() { return -1; }
virtual std::list<int> getVideoFormatList() { return std::list<int>(); }
virtual int getSampleRate() { return -1; }
virtual int getChannels() { return -1; }
virtual int getAudioFormat() { return -1; }
virtual std::list<int> getAudioFormatList() { return std::list<int>(); }
virtual ~DeviceInterface() {};
};

@ -15,6 +15,7 @@
#include "V4L2DeviceSource.h"
#include "H264_V4l2DeviceSource.h"
#include "H265_V4l2DeviceSource.h"
class DeviceSourceFactory {
public:

@ -13,30 +13,7 @@
#pragma once
// project
#include "V4L2DeviceSource.h"
// ---------------------------------
// H264 V4L2 FramedSource
// ---------------------------------
const char H264marker[] = {0,0,0,1};
const char H264shortmarker[] = {0,0,1};
class H26X_V4L2DeviceSource : public V4L2DeviceSource
{
protected:
H26X_V4L2DeviceSource(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode, bool repeatConfig, bool keepMarker)
: V4L2DeviceSource(env, device, outputFd, queueSize, captureMode), m_repeatConfig(repeatConfig), m_keepMarker(keepMarker) {}
virtual ~H26X_V4L2DeviceSource() {}
unsigned char* extractFrame(unsigned char* frame, size_t& size, size_t& outsize, int& frameType);
protected:
std::string m_sps;
std::string m_pps;
bool m_repeatConfig;
bool m_keepMarker;
};
#include "H26x_V4l2DeviceSource.h"
class H264_V4L2DeviceSource : public H26X_V4L2DeviceSource
{
@ -50,24 +27,7 @@ class H264_V4L2DeviceSource : public H26X_V4L2DeviceSource
: H26X_V4L2DeviceSource(env, device, outputFd, queueSize, captureMode, repeatConfig, keepMarker) {}
// overide V4L2DeviceSource
virtual std::list< std::pair<unsigned char*,size_t> > splitFrames(unsigned char* frame, unsigned frameSize);
virtual std::list< std::pair<unsigned char*,size_t> > splitFrames(unsigned char* frame, unsigned frameSize);
virtual std::list< std::string > getInitFrames();
virtual bool isKeyFrame(const char*, int);
};
class H265_V4L2DeviceSource : public H26X_V4L2DeviceSource
{
public:
static H265_V4L2DeviceSource* createNew(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode, bool repeatConfig, bool keepMarker) {
return new H265_V4L2DeviceSource(env, device, outputFd, queueSize, captureMode, repeatConfig, keepMarker);
}
protected:
H265_V4L2DeviceSource(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode, bool repeatConfig, bool keepMarker)
: H26X_V4L2DeviceSource(env, device, outputFd, queueSize, captureMode, repeatConfig, keepMarker) {}
// overide V4L2DeviceSource
virtual std::list< std::pair<unsigned char*,size_t> > splitFrames(unsigned char* frame, unsigned frameSize);
protected:
std::string m_vps;
};

@ -0,0 +1,37 @@
/* ---------------------------------------------------------------------------
** This software is in the public domain, furnished "as is", without technical
** support, and with no warranty, express or implied, as to its usefulness for
** any purpose.
**
** H265_V4l2DeviceSource.h
**
** H265 V4L2 live555 source
**
** -------------------------------------------------------------------------*/
#pragma once
// project
#include "H26x_V4l2DeviceSource.h"
class H265_V4L2DeviceSource : public H26X_V4L2DeviceSource
{
public:
static H265_V4L2DeviceSource* createNew(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode, bool repeatConfig, bool keepMarker) {
return new H265_V4L2DeviceSource(env, device, outputFd, queueSize, captureMode, repeatConfig, keepMarker);
}
protected:
H265_V4L2DeviceSource(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode, bool repeatConfig, bool keepMarker)
: H26X_V4L2DeviceSource(env, device, outputFd, queueSize, captureMode, repeatConfig, keepMarker) {}
// overide V4L2DeviceSource
virtual std::list< std::pair<unsigned char*,size_t> > splitFrames(unsigned char* frame, unsigned frameSize);
virtual std::list< std::string > getInitFrames();
virtual bool isKeyFrame(const char*, int);
protected:
std::string m_vps;
};

@ -0,0 +1,40 @@
/* ---------------------------------------------------------------------------
** This software is in the public domain, furnished "as is", without technical
** support, and with no warranty, express or implied, as to its usefulness for
** any purpose.
**
** H26x_V4l2DeviceSource.h
**
** H264/H265 V4L2 live555 source
**
** -------------------------------------------------------------------------*/
#pragma once
// project
#include "V4L2DeviceSource.h"
// ---------------------------------
// H264 V4L2 FramedSource
// ---------------------------------
const char H264marker[] = {0,0,0,1};
const char H264shortmarker[] = {0,0,1};
class H26X_V4L2DeviceSource : public V4L2DeviceSource
{
protected:
H26X_V4L2DeviceSource(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode, bool repeatConfig, bool keepMarker)
: V4L2DeviceSource(env, device, outputFd, queueSize, captureMode), m_repeatConfig(repeatConfig), m_keepMarker(keepMarker) {}
virtual ~H26X_V4L2DeviceSource() {}
unsigned char* extractFrame(unsigned char* frame, size_t& size, size_t& outsize, int& frameType);
std::string getFrameWithMarker(const std::string & frame);
protected:
std::string m_sps;
std::string m_pps;
bool m_repeatConfig;
bool m_keepMarker;
};

@ -11,6 +11,7 @@
**
** -------------------------------------------------------------------------*/
#pragma once
#include "RTSPServer.hh"
#include "RTSPCommon.hh"
@ -125,7 +126,11 @@ class HTTPServer : public RTSPServer
{
public:
HTTPClientConnection(RTSPServer& ourServer, int clientSocket, struct SOCKETCLIENT clientAddr, Boolean useTLS)
#if LIVEMEDIA_LIBRARY_VERSION_INT >= 1642723200
: RTSPServer::RTSPClientConnection(ourServer, clientSocket, clientAddr, useTLS), m_TCPSink(NULL), m_StreamToken(NULL), m_Subsession(NULL), m_Source(NULL) {
#else
: RTSPServer::RTSPClientConnection(ourServer, clientSocket, clientAddr), m_TCPSink(NULL), m_StreamToken(NULL), m_Subsession(NULL), m_Source(NULL) {
#endif
}
virtual ~HTTPClientConnection();

@ -14,7 +14,7 @@
// -----------------------------------------
// ServerMediaSubsession for Multicast
// -----------------------------------------
class MulticastServerMediaSubsession : public PassiveServerMediaSubsession , public BaseServerMediaSubsession
class MulticastServerMediaSubsession : public BaseServerMediaSubsession, public PassiveServerMediaSubsession
{
public:
static MulticastServerMediaSubsession* createNew(UsageEnvironment& env
@ -29,9 +29,10 @@ class MulticastServerMediaSubsession : public PassiveServerMediaSubsession , pub
, Port rtpPortNum, Port rtcpPortNum
, int ttl
, StreamReplicator* replicator)
: PassiveServerMediaSubsession(*this->createRtpSink(env, destinationAddress, rtpPortNum, rtcpPortNum, ttl, replicator)
: BaseServerMediaSubsession(replicator)
, PassiveServerMediaSubsession(*this->createRtpSink(env, destinationAddress, rtpPortNum, rtcpPortNum, ttl, replicator)
, m_rtcpInstance)
, BaseServerMediaSubsession(replicator) {};
{}
virtual char const* sdpLines() ;
virtual char const* getAuxSDPLine(RTPSink* rtpSink,FramedSource* inputSource);

@ -14,14 +14,14 @@
// -----------------------------------------
// ServerMediaSubsession for Unicast
// -----------------------------------------
class UnicastServerMediaSubsession : public OnDemandServerMediaSubsession , public BaseServerMediaSubsession
class UnicastServerMediaSubsession : public BaseServerMediaSubsession, public OnDemandServerMediaSubsession
{
public:
static UnicastServerMediaSubsession* createNew(UsageEnvironment& env, StreamReplicator* replicator);
protected:
UnicastServerMediaSubsession(UsageEnvironment& env, StreamReplicator* replicator)
: OnDemandServerMediaSubsession(env, False), BaseServerMediaSubsession(replicator) {}
: BaseServerMediaSubsession(replicator), OnDemandServerMediaSubsession(env, False) {}
virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate);
virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource);

@ -77,9 +77,10 @@ class V4L2DeviceSource: public FramedSource
public:
static V4L2DeviceSource* createNew(UsageEnvironment& env, DeviceInterface * device, int outputFd, unsigned int queueSize, CaptureMode captureMode) ;
std::string getAuxLine() { return m_auxLine; }
void setAuxLine(const std::string auxLine) { m_auxLine = auxLine; }
DeviceInterface* getDevice() { return m_device; }
void postFrame(char * frame, int frameSize, const timeval &ref);
virtual std::list< std::string > getInitFrames() { return std::list< std::string >(); }
virtual bool isKeyFrame(const char*, int) { return false; }
protected:

@ -48,43 +48,6 @@ class V4l2RTSPServer {
delete scheduler;
}
int addSession(const std::string & sessionName, ServerMediaSubsession* subSession)
{
std::list<ServerMediaSubsession*> subSessionList;
if (subSession) {
subSessionList.push_back(subSession);
}
return this->addSession(sessionName, subSessionList);
}
int addSession(const std::string & sessionName, const std::list<ServerMediaSubsession*> & subSession)
{
int nbSubsession = 0;
if (subSession.empty() == false)
{
ServerMediaSession* sms = ServerMediaSession::createNew(*m_env, sessionName.c_str());
if (sms != NULL)
{
std::list<ServerMediaSubsession*>::const_iterator subIt;
for (subIt = subSession.begin(); subIt != subSession.end(); ++subIt)
{
sms->addSubsession(*subIt);
nbSubsession++;
}
m_rtspServer->addServerMediaSession(sms);
char* url = m_rtspServer->rtspURL(sms);
if (url != NULL)
{
LOG(NOTICE) << "Play this stream using the URL \"" << url << "\"";
delete[] url;
}
}
}
return nbSubsession;
}
bool available() {
return ((m_env != NULL) && (m_rtspServer != NULL));
}
@ -124,12 +87,18 @@ class V4l2RTSPServer {
StreamReplicator* CreateAudioReplicator(
const std::string& audioDev, const std::list<snd_pcm_format_t>& audioFmtList, int audioFreq, int audioNbChannels, int verbose,
int queueSize, V4L2DeviceSource::CaptureMode captureMode);
static std::string getV4l2Alsa(const std::string& v4l2device);
static snd_pcm_format_t decodeAudioFormat(const std::string& fmt);
static std::string getAudioFormatName(const snd_pcm_format_t fmt) {
return snd_pcm_format_name(fmt);
}
#endif
// -----------------------------------------
// Add unicast Session
// -----------------------------------------
int AddUnicastSession(const std::string& url, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
ServerMediaSession* AddUnicastSession(const std::string& url, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
// Create Unicast Session
std::list<ServerMediaSubsession*> subSession;
if (videoReplicator)
@ -146,13 +115,13 @@ class V4l2RTSPServer {
// -----------------------------------------
// Add HLS & MPEG# Session
// -----------------------------------------
int AddHlsSession(const std::string& url, int hlsSegment, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
ServerMediaSession* AddHlsSession(const std::string& url, int hlsSegment, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
std::list<ServerMediaSubsession*> subSession;
if (videoReplicator)
{
subSession.push_back(TSServerMediaSubsession::createNew(*this->env(), videoReplicator, audioReplicator, hlsSegment));
}
int nbSource = this->addSession(url, subSession);
ServerMediaSession* sms = this->addSession(url, subSession);
struct in_addr ip;
#if LIVEMEDIA_LIBRARY_VERSION_INT < 1611878400
@ -163,14 +132,14 @@ class V4l2RTSPServer {
LOG(NOTICE) << "HLS http://" << inet_ntoa(ip) << ":" << m_rtspPort << "/" << url << ".m3u8";
LOG(NOTICE) << "MPEG-DASH http://" << inet_ntoa(ip) << ":" << m_rtspPort << "/" << url << ".mpd";
return nbSource;
return sms;
}
// -----------------------------------------
// Add multicats Session
// -----------------------------------------
int AddMulticastSession(const std::string& url, in_addr destinationAddress, unsigned short & rtpPortNum, unsigned short & rtcpPortNum, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
ServerMediaSession* AddMulticastSession(const std::string& url, in_addr destinationAddress, unsigned short & rtpPortNum, unsigned short & rtcpPortNum, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
LOG(NOTICE) << "RTP address " << inet_ntoa(destinationAddress) << ":" << rtpPortNum;
LOG(NOTICE) << "RTCP address " << inet_ntoa(destinationAddress) << ":" << rtcpPortNum;
@ -195,6 +164,63 @@ class V4l2RTSPServer {
return this->addSession(url, subSession);
}
std::string decodeMulticastUrl(const std::string & maddr, in_addr & destinationAddress, unsigned short & rtpPortNum, unsigned short & rtcpPortNum)
{
std::istringstream is(maddr);
std::string ip;
getline(is, ip, ':');
if (!ip.empty())
{
destinationAddress.s_addr = inet_addr(ip.c_str());
} else {
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*this->env());
}
rtpPortNum = 20000;
std::string port;
getline(is, port, ':');
if (!port.empty())
{
rtpPortNum = atoi(port.c_str());
}
rtcpPortNum = rtpPortNum+1;
getline(is, port, ':');
if (!port.empty())
{
rtcpPortNum = atoi(port.c_str());
}
return inet_ntoa(destinationAddress) + std::string(":") + std::to_string(rtpPortNum) + std::string(":") + std::to_string(rtcpPortNum);
}
ServerMediaSession* AddMulticastSession(const std::string& url, const std::string& inmulticasturi, std::string& outmulticasturi, StreamReplicator* videoReplicator, StreamReplicator* audioReplicator) {
struct in_addr destinationAddress;
unsigned short rtpPortNum;
unsigned short rtcpPortNum;
outmulticasturi = this->decodeMulticastUrl(inmulticasturi, destinationAddress, rtpPortNum, rtcpPortNum);
return this->AddMulticastSession(url, destinationAddress, rtpPortNum, rtcpPortNum, videoReplicator, audioReplicator);
}
// -----------------------------------------
// get rtsp url
// -----------------------------------------
std::string getRtspUrl(ServerMediaSession* sms) {
std::string url;
char* rtspurl = m_rtspServer->rtspURL(sms);
if (rtspurl != NULL)
{
url = rtspurl;
delete[] rtspurl;
}
return url;
}
int numClientSessions() {
return m_rtspServer->numClientSessions();
}
void RemoveSession(ServerMediaSession* sms) {
m_rtspServer->deleteServerMediaSession(sms);
}
protected:
UserAuthenticationDatabase* createUserAuthenticationDatabase(const std::list<std::string> & userPasswordList, const char* realm)
{
@ -218,6 +244,42 @@ class V4l2RTSPServer {
return auth;
}
ServerMediaSession* addSession(const std::string & sessionName, ServerMediaSubsession* subSession)
{
std::list<ServerMediaSubsession*> subSessionList;
if (subSession) {
subSessionList.push_back(subSession);
}
return this->addSession(sessionName, subSessionList);
}
ServerMediaSession* addSession(const std::string & sessionName, const std::list<ServerMediaSubsession*> & subSession)
{
ServerMediaSession* sms = NULL;
if (subSession.empty() == false)
{
sms = ServerMediaSession::createNew(*m_env, sessionName.c_str());
if (sms != NULL)
{
std::list<ServerMediaSubsession*>::const_iterator subIt;
for (subIt = subSession.begin(); subIt != subSession.end(); ++subIt)
{
sms->addSubsession(*subIt);
}
m_rtspServer->addServerMediaSession(sms);
char* url = m_rtspServer->rtspURL(sms);
if (url != NULL)
{
LOG(NOTICE) << "Play this stream using the URL \"" << url << "\"";
delete[] url;
}
}
}
return sms;
}
protected:
char m_stop;
UsageEnvironment* m_env;

@ -6,10 +6,10 @@
</style>
<body>
<!--fill streamList variable with the list of streams -->
<script src="/getStreamList?streamList" ></script>
<script src="getStreamList?streamList" ></script>
<h3>HLS</h3>
<div id="videos"></div>
<script src="hls.js/dist/hls.light.min.js" ></script>
<script src="hls.js" ></script>
<script>
if (Hls.isSupported()) {
var videos = document.getElementById("videos")

@ -0,0 +1 @@
Subproject commit 3eff050e79e76eecd240a07e5406f0787ca4af3f

@ -45,29 +45,6 @@ void sighandler(int n)
quit =1;
}
// -------------------------------------------------------
// decode multicast url <group>:<rtp_port>:<rtcp_port>
// -------------------------------------------------------
void decodeMulticastUrl(const std::string & maddr, in_addr & destinationAddress, unsigned short & rtpPortNum, unsigned short & rtcpPortNum)
{
std::istringstream is(maddr);
std::string ip;
getline(is, ip, ':');
if (!ip.empty())
{
destinationAddress.s_addr = inet_addr(ip.c_str());
}
std::string port;
getline(is, port, ':');
rtpPortNum = 20000;
if (!port.empty())
{
rtpPortNum = atoi(port.c_str());
}
rtcpPortNum = rtpPortNum+1;
}
// -------------------------------------------------------
// split video,audio device
// -------------------------------------------------------
@ -88,35 +65,6 @@ std::string getDeviceName(const std::string & devicePath)
return deviceName;
}
#ifdef HAVE_ALSA
snd_pcm_format_t decodeAudioFormat(const std::string& fmt)
{
snd_pcm_format_t audioFmt = SND_PCM_FORMAT_UNKNOWN;
if (fmt == "S16_BE") {
audioFmt = SND_PCM_FORMAT_S16_BE;
} else if (fmt == "S16_LE") {
audioFmt = SND_PCM_FORMAT_S16_LE;
} else if (fmt == "S24_BE") {
audioFmt = SND_PCM_FORMAT_S24_BE;
} else if (fmt == "S24_LE") {
audioFmt = SND_PCM_FORMAT_S24_LE;
} else if (fmt == "S32_BE") {
audioFmt = SND_PCM_FORMAT_S32_BE;
} else if (fmt == "S32_LE") {
audioFmt = SND_PCM_FORMAT_S32_LE;
} else if (fmt == "ALAW") {
audioFmt = SND_PCM_FORMAT_A_LAW;
} else if (fmt == "MULAW") {
audioFmt = SND_PCM_FORMAT_MU_LAW;
} else if (fmt == "S8") {
audioFmt = SND_PCM_FORMAT_S8;
} else if (fmt == "MPEG") {
audioFmt = SND_PCM_FORMAT_MPEG;
}
return audioFmt;
}
#endif
// -----------------------------------------
// entry point
@ -165,7 +113,7 @@ int main(int argc, char** argv)
// decode parameters
int c = 0;
while ((c = getopt (argc, argv, "v::Q:O:b:" "I:P:p:m::u:M:ct:S::x:" "R:U:" "rwBsf::F:W:H:G:" "A:C:a:" "Vh")) != -1)
while ((c = getopt (argc, argv, "v::Q:O:b:" "I:P:p:m::u:M::ct:S::x:" "R:U:" "rwBsf::F:W:H:G:" "A:C:a:" "Vh")) != -1)
{
switch (c)
{
@ -180,7 +128,7 @@ int main(int argc, char** argv)
case 'p': rtspOverHTTPPort = atoi(optarg); break;
case 'u': url = optarg; break;
case 'm': multicast = true; murl = optarg ? optarg : murl; break;
case 'M': multicast = true; maddr = optarg; break;
case 'M': multicast = true; maddr = optarg ? optarg : maddr; break;
case 'c': repeatConfig = false; break;
case 't': timeout = atoi(optarg); break;
case 'S': hlsSegment = optarg ? atoi(optarg) : defaultHlsSegment; break;
@ -205,7 +153,7 @@ int main(int argc, char** argv)
#ifdef HAVE_ALSA
case 'A': audioFreq = atoi(optarg); break;
case 'C': audioNbChannels = atoi(optarg); break;
case 'a': audioFmt = decodeAudioFormat(optarg); if (audioFmt != SND_PCM_FORMAT_UNKNOWN) {audioFmtList.push_back(audioFmt);} ; break;
case 'a': audioFmt = V4l2RTSPServer::decodeAudioFormat(optarg); if (audioFmt != SND_PCM_FORMAT_UNKNOWN) {audioFmtList.push_back(audioFmt);} ; break;
#endif
// version
@ -279,9 +227,11 @@ int main(int argc, char** argv)
// default format tries
if ((videoformatList.empty()) && (format!=0)) {
videoformatList.push_back(V4L2_PIX_FMT_HEVC);
videoformatList.push_back(V4L2_PIX_FMT_H264);
videoformatList.push_back(V4L2_PIX_FMT_MJPEG);
videoformatList.push_back(V4L2_PIX_FMT_JPEG);
videoformatList.push_back(V4L2_PIX_FMT_NV12);
}
#ifdef HAVE_ALSA
@ -307,10 +257,9 @@ int main(int argc, char** argv)
{
// decode multicast info
struct in_addr destinationAddress;
destinationAddress.s_addr = chooseRandomIPv4SSMAddress(*rtspServer.env());
unsigned short rtpPortNum = 20000;
unsigned short rtcpPortNum = rtpPortNum+1;
decodeMulticastUrl(maddr, destinationAddress, rtpPortNum, rtcpPortNum);
unsigned short rtpPortNum;
unsigned short rtcpPortNum;
rtspServer.decodeMulticastUrl(maddr, destinationAddress, rtpPortNum, rtcpPortNum);
std::list<V4l2Output*> outList;
int nbSource = 0;
@ -329,7 +278,8 @@ int main(int argc, char** argv)
{
baseUrl = getDeviceName(videoDev);
baseUrl.append("_");
output.append(getDeviceName(videoDev));
// output is not compatible with multiple device
output.assign("");
}
V4l2Output* out = NULL;
@ -354,17 +304,26 @@ int main(int argc, char** argv)
// Create Multicast Session
if (multicast)
{
nbSource += rtspServer.AddMulticastSession(baseUrl+murl, destinationAddress, rtpPortNum, rtcpPortNum, videoReplicator, audioReplicator);
ServerMediaSession* sms = rtspServer.AddMulticastSession(baseUrl+murl, destinationAddress, rtpPortNum, rtcpPortNum, videoReplicator, audioReplicator);
if (sms) {
nbSource += sms->numSubsessions();
}
}
// Create HLS Session
if (hlsSegment > 0)
{
nbSource += rtspServer.AddHlsSession(baseUrl+tsurl, hlsSegment, videoReplicator, audioReplicator);
ServerMediaSession* sms = rtspServer.AddHlsSession(baseUrl+tsurl, hlsSegment, videoReplicator, audioReplicator);
if (sms) {
nbSource += sms->numSubsessions();
}
}
// Create Unicast Session
nbSource += rtspServer.AddUnicastSession(baseUrl+url, videoReplicator, audioReplicator);
ServerMediaSession* sms = rtspServer.AddUnicastSession(baseUrl+url, videoReplicator, audioReplicator);
if (sms) {
nbSource += sms->numSubsessions();
}
}
if (nbSource>0)

@ -0,0 +1,25 @@
From: Michel Promonet <michel.promonet@free.fr>
Subject: [PATCH] Fix crash formating time_t as long (it is a long long)
Signed-off-by: Michel Promonet <michel.promonet@free.fr>
---
--- a/live/liveMedia/ServerMediaSession.cpp
+++ b/live/liveMedia/ServerMediaSession.cpp
@@ -272,7 +272,7 @@ char* ServerMediaSession::generateSDPDes
char const* const sdpPrefixFmt =
"v=0\r\n"
- "o=- %ld%06ld %d IN %s %s\r\n"
+ "o=- %lld%06lld %d IN %s %s\r\n"
"s=%s\r\n"
"i=%s\r\n"
"t=0 0\r\n"
@@ -300,7 +300,7 @@ char* ServerMediaSession::generateSDPDes
// Generate the SDP prefix (session-level lines):
snprintf(sdp, sdpLength, sdpPrefixFmt,
- fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
+ (long long)fCreationTime.tv_sec, (long long)fCreationTime.tv_usec, // o= <session id>
1, // o= <version> // (needs to change if params are modified)
addressFamily == AF_INET ? "IP4" : "IP6", // o= <address family>
ipAddressStr.val(), // o= <address>

@ -1,5 +1,6 @@
name: v4l2-rtspserver
base: core22
version: git
summary: RTSP Server for V4L2 device capture supporting HEVC/H264/JPEG/VP8/VP9
description: |
@ -18,6 +19,8 @@
grade: stable
confinement: strict
license: Unlicense
contact: michel.promonet@free.fr
parts:
v4l2rtspserver:
@ -26,16 +29,15 @@
source-type: git
build-packages:
- g++
- make
- pkg-config
- liblog4cpp5-dev
- libasound2-dev
- libssl-dev
stage-packages:
- liblog4cpp5v5
- libasound2
- libssl3
apps:
v4l2rtspserver:
command: v4l2rtspserver -b $SNAP/share/v4l2rtspserver/ -P "$(snapctl get rtsp-port)" "$(snapctl get devices)"
command: usr/local/bin/v4l2rtspserver -b $SNAP/usr/local/share/v4l2rtspserver/
daemon: simple
plugs: [network-bind]
plugs: [network-bind, camera, alsa]

@ -15,6 +15,46 @@
#include "ALSACapture.h"
static const snd_pcm_format_t formats[] = {
SND_PCM_FORMAT_S8,
SND_PCM_FORMAT_U8,
SND_PCM_FORMAT_S16_LE,
SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE,
SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_S24_LE,
SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_U24_LE,
SND_PCM_FORMAT_U24_BE,
SND_PCM_FORMAT_S32_LE,
SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_U32_LE,
SND_PCM_FORMAT_U32_BE,
SND_PCM_FORMAT_FLOAT_LE,
SND_PCM_FORMAT_FLOAT_BE,
SND_PCM_FORMAT_FLOAT64_LE,
SND_PCM_FORMAT_FLOAT64_BE,
SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
SND_PCM_FORMAT_MU_LAW,
SND_PCM_FORMAT_A_LAW,
SND_PCM_FORMAT_IMA_ADPCM,
SND_PCM_FORMAT_MPEG,
SND_PCM_FORMAT_GSM,
SND_PCM_FORMAT_SPECIAL,
SND_PCM_FORMAT_S24_3LE,
SND_PCM_FORMAT_S24_3BE,
SND_PCM_FORMAT_U24_3LE,
SND_PCM_FORMAT_U24_3BE,
SND_PCM_FORMAT_S20_3LE,
SND_PCM_FORMAT_S20_3BE,
SND_PCM_FORMAT_U20_3LE,
SND_PCM_FORMAT_U20_3BE,
SND_PCM_FORMAT_S18_3LE,
SND_PCM_FORMAT_S18_3BE,
SND_PCM_FORMAT_U18_3LE,
SND_PCM_FORMAT_U18_3BE,
};
ALSACapture* ALSACapture::createNew(const ALSACaptureParameters & params)
{
@ -101,6 +141,15 @@ ALSACapture::ALSACapture(const ALSACaptureParameters & params) : m_pcm(NULL), m_
this->close();
}
if (!err) {
// get supported format
for (int i = 0; i < sizeof(formats)/sizeof(formats[0]); ++i) {
if (!snd_pcm_hw_params_test_format(m_pcm, hw_params, formats[i])) {
m_fmtList.push_back(formats[i]);
}
}
}
LOG(NOTICE) << "ALSA device: \"" << m_params.m_devName << "\" buffer_size:" << m_bufferSize << " period_size:" << m_periodSize << " rate:" << m_params.m_sampleRate;
}
@ -128,8 +177,7 @@ size_t ALSACapture::read(char* buffer, size_t bufferSize)
int fmt_phys_width_bytes = 0;
if (m_pcm != 0)
{
int fmt_phys_width_bits = snd_pcm_format_physical_width(m_fmt);
fmt_phys_width_bytes = fmt_phys_width_bits / 8;
fmt_phys_width_bytes = snd_pcm_format_physical_width(m_fmt) / 8;
snd_pcm_sframes_t ret = snd_pcm_readi (m_pcm, buffer, m_periodSize*fmt_phys_width_bytes);
LOG(DEBUG) << "ALSA buffer in_size:" << m_periodSize*fmt_phys_width_bytes << " read_size:" << ret;

@ -72,107 +72,19 @@ std::list< std::pair<unsigned char*,size_t> > H264_V4L2DeviceSource::splitFrames
return frameList;
}
// split packet in frames
std::list< std::pair<unsigned char*,size_t> > H265_V4L2DeviceSource::splitFrames(unsigned char* frame, unsigned frameSize)
{
std::list< std::pair<unsigned char*,size_t> > frameList;
size_t bufSize = frameSize;
size_t size = 0;
int frameType = 0;
unsigned char* buffer = this->extractFrame(frame, bufSize, size, frameType);
while (buffer != NULL)
{
switch ((frameType&0x7E)>>1)
{
case 32: LOG(INFO) << "VPS size:" << size << " bufSize:" << bufSize; m_vps.assign((char*)buffer,size); break;
case 33: LOG(INFO) << "SPS size:" << size << " bufSize:" << bufSize; m_sps.assign((char*)buffer,size); break;
case 34: LOG(INFO) << "PPS size:" << size << " bufSize:" << bufSize; m_pps.assign((char*)buffer,size); break;
case 19:
case 20: LOG(INFO) << "IDR size:" << size << " bufSize:" << bufSize;
if (m_repeatConfig && !m_vps.empty() && !m_sps.empty() && !m_pps.empty())
{
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_vps.c_str(), m_vps.size()));
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_sps.c_str(), m_sps.size()));
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_pps.c_str(), m_pps.size()));
}
break;
default: break;
}
if (!m_vps.empty() && !m_sps.empty() && !m_pps.empty())
{
char* vps_base64 = base64Encode(m_vps.c_str(), m_vps.size());
char* sps_base64 = base64Encode(m_sps.c_str(), m_sps.size());
char* pps_base64 = base64Encode(m_pps.c_str(), m_pps.size());
std::ostringstream os;
os << "sprop-vps=" << vps_base64;
os << ";sprop-sps=" << sps_base64;
os << ";sprop-pps=" << pps_base64;
m_auxLine.assign(os.str());
delete [] vps_base64;
delete [] sps_base64;
delete [] pps_base64;
}
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
buffer = this->extractFrame(&buffer[size], bufSize, size, frameType);
}
std::list< std::string > H264_V4L2DeviceSource::getInitFrames() {
std::list< std::string > frameList;
frameList.push_back(this->getFrameWithMarker(m_sps));
frameList.push_back(this->getFrameWithMarker(m_pps));
return frameList;
}
// extract a frame
unsigned char* H26X_V4L2DeviceSource::extractFrame(unsigned char* frame, size_t& size, size_t& outsize, int& frameType)
{
unsigned char * outFrame = NULL;
outsize = 0;
unsigned int markerlength = 0;
frameType = 0;
unsigned char *startFrame = (unsigned char*)memmem(frame,size,H264marker,sizeof(H264marker));
if (startFrame != NULL) {
markerlength = sizeof(H264marker);
} else {
startFrame = (unsigned char*)memmem(frame,size,H264shortmarker,sizeof(H264shortmarker));
if (startFrame != NULL) {
markerlength = sizeof(H264shortmarker);
}
}
if (startFrame != NULL) {
frameType = startFrame[markerlength];
int remainingSize = size-(startFrame-frame+markerlength);
unsigned char *endFrame = (unsigned char*)memmem(&startFrame[markerlength], remainingSize, H264marker, sizeof(H264marker));
if (endFrame == NULL) {
endFrame = (unsigned char*)memmem(&startFrame[markerlength], remainingSize, H264shortmarker, sizeof(H264shortmarker));
}
if (m_keepMarker)
{
size -= startFrame-frame;
outFrame = startFrame;
}
else
{
size -= startFrame-frame+markerlength;
outFrame = &startFrame[markerlength];
}
if (endFrame != NULL)
{
outsize = endFrame - outFrame;
}
else
{
outsize = size;
}
size -= outsize;
} else if (size>= sizeof(H264shortmarker)) {
LOG(INFO) << "No marker found";
bool H264_V4L2DeviceSource::isKeyFrame(const char* buffer, int size) {
bool res = false;
if (size > 4)
{
int frameType = buffer[4]&0x1F;
res = (frameType == 5);
}
return outFrame;
}
return res;
}

@ -0,0 +1,88 @@
/* ---------------------------------------------------------------------------
** This software is in the public domain, furnished "as is", without technical
** support, and with no warranty, express or implied, as to its usefulness for
** any purpose.
**
** H265_V4l2DeviceSource.cpp
**
** H265 V4L2 Live555 source
**
** -------------------------------------------------------------------------*/
#include <sstream>
// live555
#include <Base64.hh>
// project
#include "logger.h"
#include "H265_V4l2DeviceSource.h"
// split packet in frames
std::list< std::pair<unsigned char*,size_t> > H265_V4L2DeviceSource::splitFrames(unsigned char* frame, unsigned frameSize)
{
std::list< std::pair<unsigned char*,size_t> > frameList;
size_t bufSize = frameSize;
size_t size = 0;
int frameType = 0;
unsigned char* buffer = this->extractFrame(frame, bufSize, size, frameType);
while (buffer != NULL)
{
switch ((frameType&0x7E)>>1)
{
case 32: LOG(INFO) << "VPS size:" << size << " bufSize:" << bufSize; m_vps.assign((char*)buffer,size); break;
case 33: LOG(INFO) << "SPS size:" << size << " bufSize:" << bufSize; m_sps.assign((char*)buffer,size); break;
case 34: LOG(INFO) << "PPS size:" << size << " bufSize:" << bufSize; m_pps.assign((char*)buffer,size); break;
case 19:
case 20: LOG(INFO) << "IDR size:" << size << " bufSize:" << bufSize;
if (m_repeatConfig && !m_vps.empty() && !m_sps.empty() && !m_pps.empty())
{
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_vps.c_str(), m_vps.size()));
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_sps.c_str(), m_sps.size()));
frameList.push_back(std::pair<unsigned char*,size_t>((unsigned char*)m_pps.c_str(), m_pps.size()));
}
break;
default: break;
}
if (!m_vps.empty() && !m_sps.empty() && !m_pps.empty())
{
char* vps_base64 = base64Encode(m_vps.c_str(), m_vps.size());
char* sps_base64 = base64Encode(m_sps.c_str(), m_sps.size());
char* pps_base64 = base64Encode(m_pps.c_str(), m_pps.size());
std::ostringstream os;
os << "sprop-vps=" << vps_base64;
os << ";sprop-sps=" << sps_base64;
os << ";sprop-pps=" << pps_base64;
m_auxLine.assign(os.str());
delete [] vps_base64;
delete [] sps_base64;
delete [] pps_base64;
}
frameList.push_back(std::pair<unsigned char*,size_t>(buffer, size));
buffer = this->extractFrame(&buffer[size], bufSize, size, frameType);
}
return frameList;
}
std::list< std::string > H265_V4L2DeviceSource::getInitFrames() {
std::list< std::string > frameList;
frameList.push_back(this->getFrameWithMarker(m_vps));
frameList.push_back(this->getFrameWithMarker(m_sps));
frameList.push_back(this->getFrameWithMarker(m_pps));
return frameList;
}
bool H265_V4L2DeviceSource::isKeyFrame(const char* buffer, int size) {
bool res = false;
if (size > 4)
{
int frameType = (buffer[4]&0x7E)>>1;
res = (frameType == 19 || frameType == 20);
}
return res;
}

@ -0,0 +1,80 @@
/* ---------------------------------------------------------------------------
** This software is in the public domain, furnished "as is", without technical
** support, and with no warranty, express or implied, as to its usefulness for
** any purpose.
**
** H26x_V4l2DeviceSource.cpp
**
** H264/H265 V4L2 Live555 source
**
** -------------------------------------------------------------------------*/
#include <sstream>
// live555
#include <Base64.hh>
// project
#include "logger.h"
#include "H26x_V4l2DeviceSource.h"
// extract a frame
unsigned char* H26X_V4L2DeviceSource::extractFrame(unsigned char* frame, size_t& size, size_t& outsize, int& frameType)
{
unsigned char * outFrame = NULL;
outsize = 0;
unsigned int markerlength = 0;
frameType = 0;
unsigned char *startFrame = (unsigned char*)memmem(frame,size,H264marker,sizeof(H264marker));
if (startFrame != NULL) {
markerlength = sizeof(H264marker);
} else {
startFrame = (unsigned char*)memmem(frame,size,H264shortmarker,sizeof(H264shortmarker));
if (startFrame != NULL) {
markerlength = sizeof(H264shortmarker);
}
}
if (startFrame != NULL) {
frameType = startFrame[markerlength];
int remainingSize = size-(startFrame-frame+markerlength);
unsigned char *endFrame = (unsigned char*)memmem(&startFrame[markerlength], remainingSize, H264marker, sizeof(H264marker));
if (endFrame == NULL) {
endFrame = (unsigned char*)memmem(&startFrame[markerlength], remainingSize, H264shortmarker, sizeof(H264shortmarker));
}
if (m_keepMarker)
{
size -= startFrame-frame;
outFrame = startFrame;
}
else
{
size -= startFrame-frame+markerlength;
outFrame = &startFrame[markerlength];
}
if (endFrame != NULL)
{
outsize = endFrame - outFrame;
}
else
{
outsize = size;
}
size -= outsize;
} else if (size>= sizeof(H264shortmarker)) {
LOG(INFO) << "No marker found";
}
return outFrame;
}
std::string H26X_V4L2DeviceSource::getFrameWithMarker(const std::string & frame) {
std::string frameWithMarker;
frameWithMarker.append(H264marker, sizeof(H264marker));
frameWithMarker.append(frame);
return frameWithMarker;
}

@ -48,17 +48,24 @@ void MJPEGVideoSource::afterGettingFrame(unsigned frameSize,unsigned numTruncate
int length = (fTo[i+2]<<8)|(fTo[i+3]);
LOG(DEBUG) << "DQT length:" << length;
unsigned int precision = (fTo[i+4]&0xf0)<<4;
unsigned int quantIdx = fTo[i+4]&0x0f;
unsigned int quantSize = 64*(precision+1);
if (quantSize*quantIdx+quantSize <= sizeof(m_qTable)) {
if ( (i+2+length) < frameSize) {
memcpy(m_qTable + quantSize*quantIdx, fTo + i + 5, length-3);
LOG(DEBUG) << "Quantization table idx:" << quantIdx << " precision:" << precision << " size:" << quantSize << " total size:" << m_qTableSize;
if (quantSize*quantIdx+quantSize > m_qTableSize) {
int qtable_length = length-2;
unsigned int qtable_position = i+4;
while (qtable_length > 0) {
LOG(DEBUG) << "DQT qtable_length:" << qtable_length;
unsigned int precision = (fTo[qtable_position]&0xf0)<<4;
unsigned int quantIdx = fTo[qtable_position]&0x0f;
unsigned int quantSize = 64*(precision+1);
if (quantSize*quantIdx+quantSize <= sizeof(m_qTable)) {
if ( (qtable_position+quantSize) < frameSize) {
memcpy(m_qTable + quantSize*quantIdx, fTo + qtable_position + 1, quantSize);
LOG(DEBUG) << "Quantization table idx:" << quantIdx << " precision:" << precision << " size:" << quantSize << " total size:" << m_qTableSize;
if (quantSize*quantIdx+quantSize > m_qTableSize) {
m_qTableSize = quantSize*quantIdx+quantSize;
}
}
}
}
qtable_length -= quantSize+1;
qtable_position += quantSize+1;
}
i+=length+2;

@ -81,8 +81,12 @@ RTPSink* BaseServerMediaSubsession::createSink(UsageEnvironment& env, Groupsock
DeviceInterface* device = source->getDevice();
switch (device->getVideoFormat()) {
case V4L2_PIX_FMT_YUV444: sampling = "YCbCr-4:4:4"; break;
case V4L2_PIX_FMT_YUYV: sampling = "YCbCr-4:2:2"; break;
case V4L2_PIX_FMT_UYVY: sampling = "YCbCr-4:2:2"; break;
case V4L2_PIX_FMT_UYVY : sampling = "YCbCr-4:2:2"; break;
case V4L2_PIX_FMT_NV12 : sampling = "YCbCr-4:2:0"; break;
case V4L2_PIX_FMT_RGB24 : sampling = "RGB" ; break;
case V4L2_PIX_FMT_RGB32 : sampling = "RGBA" ; break;
case V4L2_PIX_FMT_BGR24 : sampling = "BGR" ; break;
case V4L2_PIX_FMT_BGR32 : sampling = "BGRA" ; break;
}
videoSink = RawVideoRTPSink::createNew(env, rtpGroupsock, rtpPayloadTypeIfDynamic, device->getWidth(), device->getHeight(), 8, sampling.c_str(),"BT709-2");
}
@ -99,6 +103,10 @@ RTPSink* BaseServerMediaSubsession::createSink(UsageEnvironment& env, Groupsock
getline(is, channels);
videoSink = SimpleRTPSink::createNew(env, rtpGroupsock,rtpPayloadTypeIfDynamic, atoi(sampleRate.c_str()), "audio", "L16", atoi(channels.c_str()), True, False);
}
else if (format.find("audio/MPEG") == 0)
{
videoSink = MPEG1or2AudioRTPSink::createNew(env, rtpGroupsock);
}
return videoSink;
}

@ -103,21 +103,20 @@ void* V4L2DeviceSource::thread()
int ret = select(fd+1, &fdset, NULL, NULL, &tv);
if (ret == 1)
{
if (FD_ISSET(fd, &fdset))
LOG(DEBUG) << "waitingFrame\tdelay:" << (1000-(tv.tv_usec/1000)) << "ms";
if (this->getNextFrame() <= 0)
{
LOG(DEBUG) << "waitingFrame\tdelay:" << (1000-(tv.tv_usec/1000)) << "ms";
if (this->getNextFrame() <= 0)
if (errno == EAGAIN)
{
LOG(ERROR) << "error:" << strerror(errno);
LOG(DEBUG) << "Retrying getNextFrame";
}
else
{
LOG(ERROR) << "error:" << strerror(errno);
stop=1;
}
}
}
else if (ret == -1)
{
LOG(ERROR) << "stop " << strerror(errno);
stop=1;
}
}
LOG(NOTICE) << "end thread";
return NULL;
@ -201,10 +200,12 @@ int V4L2DeviceSource::getNextFrame()
if (frameSize < 0)
{
LOG(NOTICE) << "V4L2DeviceSource::getNextFrame errno:" << errno << " " << strerror(errno);
delete [] buffer;
}
else if (frameSize == 0)
{
LOG(NOTICE) << "V4L2DeviceSource::getNextFrame no data errno:" << errno << " " << strerror(errno);
LOG(DEBUG) << "V4L2DeviceSource::getNextFrame no data errno:" << errno << " " << strerror(errno);
delete [] buffer;
}
else
{
@ -221,12 +222,15 @@ void V4L2DeviceSource::postFrame(char * frame, int frameSize, const timeval &ref
timeval diff;
timersub(&tv,&ref,&diff);
m_in.notify(tv.tv_sec, frameSize);
LOG(DEBUG) << "getNextFrame\ttimestamp:" << ref.tv_sec << "." << ref.tv_usec << "\tsize:" << frameSize <<"\tdiff:" << (diff.tv_sec*1000+diff.tv_usec/1000) << "ms";
LOG(DEBUG) << "postFrame\ttimestamp:" << ref.tv_sec << "." << ref.tv_usec << "\tsize:" << frameSize <<"\tdiff:" << (diff.tv_sec*1000+diff.tv_usec/1000) << "ms";
processFrame(frame,frameSize,ref);
if (m_outfd != -1)
{
write(m_outfd, frame, frameSize);
int written = write(m_outfd, frame, frameSize);
if (written != frameSize) {
LOG(NOTICE) << "error writing output " << written << "/" << frameSize << " err:" << strerror(errno);
}
}
}

@ -59,7 +59,6 @@ StreamReplicator* V4l2RTSPServer::CreateVideoReplicator(
LOG(FATAL) << "No Streaming format supported for device " << videoDev;
delete videoCapture;
} else {
LOG(NOTICE) << "Create Source ..." << videoDev;
videoReplicator = DeviceSourceFactory::createStreamReplicator(this->env(), videoCapture->getFormat(), new VideoCaptureAccess(videoCapture), queueSize, captureMode, outfd, repeatConfig);
if (videoReplicator == NULL)
{
@ -102,7 +101,7 @@ std::string getDeviceId(const std::string& evt) {
return deviceid;
}
std::string getV4l2Alsa(const std::string& v4l2device) {
std::string V4l2RTSPServer::getV4l2Alsa(const std::string& v4l2device) {
std::string audioDevice(v4l2device);
std::map<std::string,std::string> videodevices;
@ -171,6 +170,33 @@ std::string getV4l2Alsa(const std::string& v4l2device) {
return audioDevice;
}
snd_pcm_format_t V4l2RTSPServer::decodeAudioFormat(const std::string& fmt)
{
snd_pcm_format_t audioFmt = SND_PCM_FORMAT_UNKNOWN;
if (fmt == "S16_BE") {
audioFmt = SND_PCM_FORMAT_S16_BE;
} else if (fmt == "S16_LE") {
audioFmt = SND_PCM_FORMAT_S16_LE;
} else if (fmt == "S24_BE") {
audioFmt = SND_PCM_FORMAT_S24_BE;
} else if (fmt == "S24_LE") {
audioFmt = SND_PCM_FORMAT_S24_LE;
} else if (fmt == "S32_BE") {
audioFmt = SND_PCM_FORMAT_S32_BE;
} else if (fmt == "S32_LE") {
audioFmt = SND_PCM_FORMAT_S32_LE;
} else if (fmt == "ALAW") {
audioFmt = SND_PCM_FORMAT_A_LAW;
} else if (fmt == "MULAW") {
audioFmt = SND_PCM_FORMAT_MU_LAW;
} else if (fmt == "S8") {
audioFmt = SND_PCM_FORMAT_S8;
} else if (fmt == "MPEG") {
audioFmt = SND_PCM_FORMAT_MPEG;
}
return audioFmt;
}
StreamReplicator* V4l2RTSPServer::CreateAudioReplicator(
const std::string& audioDev, const std::list<snd_pcm_format_t>& audioFmtList, int audioFreq, int audioNbChannels, int verbose,
int queueSize, V4L2DeviceSource::CaptureMode captureMode) {

@ -1 +0,0 @@
Subproject commit 21d4d0beac3296d044f96d300614a7505dba2280
Loading…
Cancel
Save