Compare commits

...

84 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

@ -120,8 +120,7 @@ jobs:
<<: *defaults
steps:
- setup_remote_docker:
version: 20.10.12
- setup_remote_docker
- checkout
- run: git submodule update --init
- run:
@ -138,15 +137,15 @@ jobs:
<<: *defaults
steps:
- setup_remote_docker:
version: 20.10.12
- 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
# -------------------------------------
@ -156,15 +155,15 @@ jobs:
<<: *defaults
steps:
- setup_remote_docker:
version: 20.10.12
- 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
# -------------------------------------
@ -174,15 +173,15 @@ jobs:
<<: *defaults
steps:
- setup_remote_docker:
version: 20.10.12
- 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
# -------------------------------------
@ -192,8 +191,7 @@ jobs:
<<: *defaults
steps:
- setup_remote_docker:
version: 20.10.12
- 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

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: build
run: |

@ -28,6 +28,20 @@ jobs:
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:

@ -8,19 +8,24 @@ jobs:
matrix:
include:
- os: ubuntu-latest
dockerfile: Dockerfile.arm64
image: arm64v8/ubuntu:24.04
label: arm64
- os: ubuntu-latest
dockerfile: Dockerfile.rpi
image: balenalib/raspberry-pi
label: armv6l
- os: ubuntu-latest
dockerfile: Dockerfile
image: balenalib/raspberry-pi2
label: armv7
- os: ubuntu-latest
image: ubuntu:24.04
label: amd64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Build an image from Dockerfile
run: docker build -t docker.io/${{ github.repository }}:${{ matrix.label }} -f ${{ matrix.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@v2
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

4
.gitmodules vendored

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

@ -3,23 +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")
if(NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE "Release")
endif()
set(CMAKE_CXX_STANDARD 11)
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()
@ -41,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)
@ -60,7 +61,9 @@ if (LOG4CPP)
endif ()
# openssl ?
find_package(OpenSSL QUIET)
if (WITH_SSL)
find_package(OpenSSL QUIET)
endif()
MESSAGE("OpenSSL_FOUND = ${OpenSSL_FOUND}")
# live555
@ -78,25 +81,32 @@ else()
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)
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_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
@ -104,8 +114,8 @@ 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 ()
@ -114,18 +124,20 @@ endif()
# libv4l2cpp
EXEC_PROGRAM("git submodule update --init")
add_subdirectory(libv4l2cpp EXCLUDE_FROM_ALL)
include_directories("libv4l2cpp/inc")
target_link_libraries (${PROJECT_NAME} libv4l2cpp)
target_include_directories(libv4l2rtspserver PUBLIC libv4l2cpp/inc)
target_link_libraries (libv4l2rtspserver PUBLIC libv4l2cpp ${LIBRARIES})
# static link of stdc++ if available
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-static-libgcc" CXX_SUPPORTS_STATIC_LIBGCC)
if (CXX_SUPPORTS_STATIC_LIBGCC)
target_link_libraries (${CMAKE_PROJECT_NAME} -static-libgcc)
endif()
CHECK_CXX_COMPILER_FLAG("-static-libstdc++" CXX_SUPPORTS_STATIC_LIBSTDCPP)
if (CXX_SUPPORTS_STATIC_LIBSTDCPP)
target_link_libraries (${CMAKE_PROJECT_NAME} -static-libstdc++)
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
@ -148,12 +160,17 @@ 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(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
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}-${CMAKE_BUILD_TYPE})

@ -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
====================
@ -123,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
-----------------------
@ -157,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;
}
}

@ -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;
};

@ -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:

@ -87,6 +87,12 @@ 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
// -----------------------------------------
@ -158,7 +164,42 @@ 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
// -----------------------------------------
@ -172,6 +213,9 @@ class V4l2RTSPServer {
}
return url;
}
int numClientSessions() {
return m_rtspServer->numClientSessions();
}
void RemoveSession(ServerMediaSession* sms) {
m_rtspServer->deleteServerMediaSession(sms);

@ -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")

@ -1 +1 @@
Subproject commit e06e49e2dd18082808a4f875993e722604487389
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,6 +227,7 @@ 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);
@ -308,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;

@ -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;

@ -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) {

Loading…
Cancel
Save