diff --git a/.github/workflows/ccpp.yml.bk b/.github/workflows/ccpp.yml.bk new file mode 100644 index 00000000000..60482a5a355 --- /dev/null +++ b/.github/workflows/ccpp.yml.bk @@ -0,0 +1,594 @@ +name: Darknet Continuous Integration + +on: [push, pull_request] + +jobs: + ubuntu-makefile: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Update apt + run: sudo apt update + - name: Install dependencies + run: sudo apt install libopencv-dev + + - name: 'Install CUDA' + run: | + wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.2.89-1_amd64.deb + sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub + sudo dpkg -i cuda-repo-ubuntu1804_10.2.89-1_amd64.deb + wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb + sudo dpkg -i nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb + sudo apt update + sudo apt-get install -y --no-install-recommends cuda-compiler-10-2 cuda-libraries-dev-10-2 cuda-driver-dev-10-2 cuda-cudart-dev-10-2 cuda-curand-dev-10-2 + sudo apt-get install -y --no-install-recommends libcudnn7-dev + sudo ln -s /usr/local/cuda-10.2/lib64/stubs/libcuda.so /usr/local/cuda-10.2/lib64/stubs/libcuda.so.1 + sudo ln -s /usr/local/cuda-10.2/lib64/stubs/libcuda.so /usr/local/cuda-10.2/lib64/libcuda.so.1 + sudo ln -s /usr/local/cuda-10.2/lib64/stubs/libcuda.so /usr/local/cuda-10.2/lib64/libcuda.so + sudo ln -s /usr/local/cuda-10.2 /usr/local/cuda + export PATH=/usr/local/cuda/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/local/cuda/lib64/stubs:$LD_LIBRARY_PATH + nvcc --version + gcc --version + + - name: 'LIBSO=1 GPU=0 CUDNN=0 OPENCV=0' + run: | + make LIBSO=1 GPU=0 CUDNN=0 OPENCV=0 -j 8 + make clean + - name: 'LIBSO=1 GPU=0 CUDNN=0 OPENCV=0 DEBUG=1' + run: | + make LIBSO=1 GPU=0 CUDNN=0 OPENCV=0 DEBUG=1 -j 8 + make clean + - name: 'LIBSO=1 GPU=0 CUDNN=0 OPENCV=0 AVX=1' + run: | + make LIBSO=1 GPU=0 CUDNN=0 OPENCV=0 AVX=1 -j 8 + make clean + - name: 'LIBSO=1 GPU=0 CUDNN=0 OPENCV=1' + run: | + make LIBSO=1 GPU=0 CUDNN=0 OPENCV=1 -j 8 + make clean + - name: 'LIBSO=1 GPU=1 CUDNN=1 OPENCV=1' + run: | + export PATH=/usr/local/cuda/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/local/cuda/lib64/stubs:$LD_LIBRARY_PATH + make LIBSO=1 GPU=1 CUDNN=1 OPENCV=1 -j 8 + make clean + - name: 'LIBSO=1 GPU=1 CUDNN=1 OPENCV=1 CUDNN_HALF=1' + run: | + export PATH=/usr/local/cuda/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/local/cuda/lib64/stubs:$LD_LIBRARY_PATH + make LIBSO=1 GPU=1 CUDNN=1 OPENCV=1 CUDNN_HALF=1 -j 8 + make clean + - name: 'LIBSO=1 GPU=1 CUDNN=1 OPENCV=1 CUDNN_HALF=1 USE_CPP=1' + run: | + export PATH=/usr/local/cuda/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/cuda/lib64:/usr/local/cuda/lib64/stubs:$LD_LIBRARY_PATH + make LIBSO=1 GPU=1 CUDNN=1 OPENCV=1 CUDNN_HALF=1 USE_CPP=1 -j 8 + make clean + + + ubuntu-vcpkg: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Update apt + run: sudo apt update + - name: Install dependencies + run: sudo apt install yasm + + - uses: lukka/get-cmake@latest + + - name: Restore from cache and run vcpkg + env: + vcpkgResponseFile: ${{ github.workspace }}/cmake/vcpkg_linux.diff + uses: lukka/run-vcpkg@v2 + with: + vcpkgArguments: '@${{ env.vcpkgResponseFile }}' + vcpkgDirectory: '${{ github.workspace }}/vcpkg' + vcpkgGitCommitId: '8121b4ec3d6a11353daf7639ed9082a78e617a2e' + appendedCacheKey: ${{ hashFiles(env.vcpkgResponseFile) }} + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeBuildType: 'Release' + cmakeAppendedArgs: "-DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release" + buildWithCMakeArgs: '--target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + ubuntu: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Update apt + run: sudo apt update + - name: Install dependencies + run: sudo apt install libopencv-dev + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + ubuntu-cuda: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Update apt + run: sudo apt update + - name: Install dependencies + run: sudo apt install libopencv-dev + + - uses: lukka/get-cmake@latest + + - name: 'Install CUDA' + env: + CUDACXX: "/usr/local/cuda-10.2/bin/nvcc" + CUDA_PATH: "/usr/local/cuda-10.2" + CUDA_TOOLKIT_ROOT_DIR: "/usr/local/cuda-10.2" + LD_LIBRARY_PATH: "/usr/local/cuda-10.2/lib64:/usr/local/cuda-10.2/lib64/stubs:$LD_LIBRARY_PATH" + run: | + wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.2.89-1_amd64.deb + sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub + sudo dpkg -i cuda-repo-ubuntu1804_10.2.89-1_amd64.deb + wget http://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb + sudo dpkg -i nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb + sudo apt update + sudo apt-get install -y --no-install-recommends cuda-compiler-10-2 cuda-libraries-dev-10-2 cuda-driver-dev-10-2 cuda-cudart-dev-10-2 cuda-curand-dev-10-2 + sudo apt-get install -y --no-install-recommends libcudnn7-dev + sudo ln -s /usr/local/cuda-10.2/lib64/stubs/libcuda.so /usr/local/cuda-10.2/lib64/stubs/libcuda.so.1 + + - name: 'Build with CMake and Ninja' + env: + CUDACXX: "/usr/local/cuda-10.2/bin/nvcc" + CUDA_PATH: "/usr/local/cuda-10.2" + CUDA_TOOLKIT_ROOT_DIR: "/usr/local/cuda-10.2" + LD_LIBRARY_PATH: "/usr/local/cuda-10.2/lib64:/usr/local/cuda-10.2/lib64/stubs:$LD_LIBRARY_PATH" + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-cuda-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-cuda-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-cuda-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-cuda-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + ubuntu-no-ocv-cpp: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release -DBUILD_AS_CPP:BOOL=TRUE" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--target install' + + + osx-vcpkg: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v2 + + - name: Install dependencies + run: brew install libomp yasm + + - uses: lukka/get-cmake@latest + + - name: Restore from cache and run vcpkg + env: + vcpkgResponseFile: ${{ github.workspace }}/cmake/vcpkg_osx.diff + uses: lukka/run-vcpkg@v2 + with: + vcpkgArguments: '@${{ env.vcpkgResponseFile }}' + vcpkgDirectory: '${{ github.workspace }}/vcpkg' + vcpkgGitCommitId: '8121b4ec3d6a11353daf7639ed9082a78e617a2e' + appendedCacheKey: ${{ hashFiles(env.vcpkgResponseFile) }} + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeBuildType: 'Release' + cmakeAppendedArgs: "-DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release" + buildWithCMakeArgs: '--target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + osx: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v2 + + - name: Install dependencies + run: brew install opencv libomp + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + osx-no-ocv-no-omp-cpp: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v2 + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release -DBUILD_AS_CPP:BOOL=TRUE" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--target install' + + + win-vcpkg: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + + - uses: lukka/get-cmake@latest + + - name: Restore from cache and run vcpkg + env: + vcpkgResponseFile: ${{ github.workspace }}/cmake/vcpkg_windows.diff + uses: lukka/run-vcpkg@v2 + with: + vcpkgArguments: '@${{ env.vcpkgResponseFile }}' + vcpkgDirectory: '${{ github.workspace }}/vcpkg' + vcpkgGitCommitId: '8121b4ec3d6a11353daf7639ed9082a78e617a2e' + appendedCacheKey: ${{ hashFiles(env.vcpkgResponseFile) }} + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--config Release --target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ runner.workspace }}/buildDirectory/Release/*.dll + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + win-vcpkg-cuda: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: 'Install CUDA' + run: | + choco install cuda --version=10.2.89.20191206 -y + $env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.." + Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" + refreshenv + + - uses: lukka/get-cmake@latest + + - name: Restore from cache and run vcpkg + env: + vcpkgResponseFile: ${{ github.workspace }}/cmake/vcpkg_windows_cuda.diff + CUDA_PATH: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDA_PATH_V10_2: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDA_TOOLKIT_ROOT_DIR: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDACXX: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\bin\\nvcc.exe" + + uses: lukka/run-vcpkg@v2 + with: + vcpkgArguments: '@${{ env.vcpkgResponseFile }}' + vcpkgDirectory: '${{ github.workspace }}/vcpkg' + vcpkgGitCommitId: '8121b4ec3d6a11353daf7639ed9082a78e617a2e' + appendedCacheKey: ${{ hashFiles(env.vcpkgResponseFile) }} + + - name: 'Build with CMake and Ninja' + env: + CUDA_PATH: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDA_PATH_V10_2: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDA_TOOLKIT_ROOT_DIR: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDACXX: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\bin\\nvcc.exe" + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--config Release --target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-cuda-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-cuda-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-cuda-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-cuda-${{ runner.os }} + path: ${{ runner.workspace }}/buildDirectory/Release/*.dll + - uses: actions/upload-artifact@v2 + with: + name: darknet-vcpkg-cuda-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + win-integrated-libs: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--config Release --target install' + + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: cfg + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: data + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/*dark* + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/3rdparty/pthreads/bin/*.dll + - uses: actions/upload-artifact@v2 + with: + name: darknet-${{ runner.os }} + path: ${{ github.workspace }}/uselib* + + + win-intlibs-cpp: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release -DBUILD_AS_CPP:BOOL=TRUE" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--config Release --target install' + + + win-intlibs-cuda: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: 'Install CUDA' + run: | + choco install cuda --version=10.2.89.20191206 -y + $env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.." + Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" + refreshenv + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + env: + CUDA_PATH: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDA_PATH_V10_2: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDA_TOOLKIT_ROOT_DIR: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2" + CUDACXX: "C:\\Program\ Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\bin\\nvcc.exe" + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--config Release --target install' + + + mingw: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + + - uses: lukka/get-cmake@latest + + - name: 'Build with CMake and Ninja' + uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: CMakeListsTxtAdvanced + cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' + useVcpkgToolchainFile: true + buildDirectory: '${{ runner.workspace }}/buildDirectory' + cmakeAppendedArgs: "-G\"MinGW Makefiles\" -DCMAKE_BUILD_TYPE=Release" + cmakeBuildType: 'Release' + buildWithCMakeArgs: '--config Release --target install' + + + cygwin: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + + - name: 'Install Cygwin' + run: | + choco install cygwin -y + choco install cyg-get -y + cyg-get gcc-g++ cmake make libopencv-devel libncurses-devel + + - name: 'Build' + run: | + mkdir buildDirectory + cd buildDirectory + path C:\tools\cygwin\bin + bash -c 'cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE="Release"' + bash -c 'cmake --build . --target install -- -j8' + shell: cmd diff --git a/Makefile b/Makefile index 167d071585b..e353462adec 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ OPENMP=0 LIBSO=0 ZED_CAMERA=0 ZED_CAMERA_v2_8=0 +STREAM=0 +FFMPEG=0 # set GPU=1 and CUDNN=1 to speedup on GPU # set CUDNN_HALF=1 to further speedup 3 x times (Mixed-precision on Tensor Cores) GPU: Volta, Xavier, Turing, Ampere, Ada and higher @@ -94,7 +96,21 @@ NVCC=nvcc OPTS=-Ofast LDFLAGS= -lm -pthread COMMON= -Iinclude/ -I3rdparty/stb/include -CFLAGS=-Wall -Wfatal-errors -Wno-unused-result -Wno-unknown-pragmas -fPIC -rdynamic +CFLAGS=-Wall -Wfatal-errors -Wno-unused-result -Wno-unknown-pragmas -fPIC -fpermissive -rdynamic + +ifeq ($(STREAM), 1) +COMMON+= -DSTREAM +CFLAGS+= -DSTREAM +LDFLAGS+= `pkg-config --libs libavformat libavcodec libavutil libswscale 2>/dev/null` +COMMON+= `pkg-config --cflags libavformat libavcodec libavutil libswscale 2>/dev/null` +endif + +ifeq ($(FFMPEG), 1) +COMMON+= -DFFMPEG +CFLAGS+= -DFFMPEG +LDFLAGS+= `pkg-config --libs libswresample libswscale libavutil libavcodec libavformat 2>/dev/null` +COMMON+= `pkg-config --cflags libswresample libswscale libavutil libavcodec libavformat 2>/dev/null` +endif ifeq ($(DEBUG), 1) #OPTS= -O0 -g @@ -172,6 +188,12 @@ ifeq ($(GPU), 1) LDFLAGS+= -lstdc++ OBJ+=convolutional_kernels.o activation_kernels.o im2col_kernels.o col2im_kernels.o blas_kernels.o crop_layer_kernels.o dropout_layer_kernels.o maxpool_layer_kernels.o network_kernels.o avgpool_layer_kernels.o endif +ifeq ($(STREAM), 1) +OBJ+=stream.o streamer.o +endif +ifeq ($(FFMPEG), 1) +OBJ+=image_ffmpeg.o +endif OBJS = $(addprefix $(OBJDIR), $(OBJ)) DEPS = $(wildcard src/*.h) Makefile include/darknet.h diff --git a/src/demo.c b/src/demo.c index 5a01faf36e0..e03a253a324 100644 --- a/src/demo.c +++ b/src/demo.c @@ -15,6 +15,10 @@ #include #endif +#ifdef FFMPEG +#include "image_ffmpeg.h" +#endif + #ifdef OPENCV #include "http_stream.h" @@ -54,6 +58,9 @@ static const int thread_wait_ms = 1; static volatile int run_fetch_in_thread = 0; static volatile int run_detect_in_thread = 0; +#ifdef FFMPEG +static int input_is_stream = 0; +#endif void *fetch_in_thread(void *ptr) { @@ -65,10 +72,16 @@ void *fetch_in_thread(void *ptr) this_thread_yield(); } int dont_close_stream = 0; // set 1 if your IP-camera periodically turns off and turns on video-stream - if (letter_box) + if (letter_box){ in_s = get_image_from_stream_letterbox(cap, net.w, net.h, net.c, &in_img, dont_close_stream); - else + }else{ +#ifdef FFMPEG + if (input_is_stream) in_s = get_image_from_ffmpeg_stream_resize(&in_img, net.w, net.h, net.c); + else in_s = get_image_from_stream_resize(cap, net.w, net.h, net.c, &in_img, dont_close_stream); +#else in_s = get_image_from_stream_resize(cap, net.w, net.h, net.c, &in_img, dont_close_stream); +#endif + } if (!in_s.data) { printf("Stream closed.\n"); custom_atomic_store_int(&flag_exit, 1); @@ -178,6 +191,10 @@ void demo(char *cfgfile, char *weightfile, float thresh, float hier_thresh, int if(filename){ printf("video file: %s\n", filename); +#ifdef FFMPEG + open_video_stream(filename); + input_is_stream = 1; +#endif cap = get_capture_video_stream(filename); demo_skip_frame = is_live_stream(filename); }else{ @@ -358,7 +375,6 @@ void demo(char *cfgfile, char *weightfile, float thresh, float hier_thresh, int write_frame_cv(output_video_writer, show_img); printf("\n cvWriteFrame \n"); } - while (custom_atomic_load_int(&run_detect_in_thread)) { if(avg_fps > 50) this_thread_yield(); else this_thread_sleep_for(thread_wait_ms); // custom_join(detect_thread, 0); @@ -406,6 +422,9 @@ void demo(char *cfgfile, char *weightfile, float thresh, float hier_thresh, int start_time = get_time_point(); } } +#ifdef FFMPEG + av_pkt_unref(); +#endif } printf("input video stream closed. \n"); if (output_video_writer) { diff --git a/src/detector.c b/src/detector.c index 0fc36142904..e2c3d5816bd 100644 --- a/src/detector.c +++ b/src/detector.c @@ -9,6 +9,14 @@ #include "demo.h" #include "option_list.h" +#ifdef STREAM +#include "stream.h" +#endif + +#ifdef FFMPEG +#include "image_ffmpeg.h" +#endif + #ifndef __COMPAR_FN_T #define __COMPAR_FN_T typedef int (*__compar_fn_t)(const void*, const void*); @@ -158,7 +166,6 @@ void train_detector(char *datacfg, char *cfgfile, char *weightfile, int *gpus, i args.mosaic_bound = net.mosaic_bound; args.contrastive = net.contrastive; args.contrastive_jit_flip = net.contrastive_jit_flip; - args.contrastive_color = net.contrastive_color; if (dont_show && show_imgs) show_imgs = 2; args.show_imgs = show_imgs; @@ -1979,6 +1986,15 @@ void run_detector(int argc, char **argv) int ext_output = find_arg(argc, argv, "-ext_output"); int save_labels = find_arg(argc, argv, "-save_labels"); char* chart_path = find_char_arg(argc, argv, "-chart", 0); + + int stream_bitrate = find_int_arg(argc, argv, "-stream_bitrate", 0); + int stream_frame_width = find_int_arg(argc, argv, "-stream_width", 0); + int stream_frame_height = find_int_arg(argc, argv, "-stream_height", 0); + int stream_gop_size = find_int_arg(argc, argv, "-stream_gop", 0); + int stream_fps = find_int_arg(argc, argv, "-stream_fps", 0); + char *stream_addr = find_char_arg(argc, argv, "-stream_address", 0); + char *stream_profile = find_char_arg(argc, argv, "-stream_profile", "high444"); + // While training, decide after how many epochs mAP will be calculated. Default value is 4 which means the mAP will be calculated after each 4 epochs int mAP_epochs = find_int_arg(argc, argv, "-mAP_epochs", 4); if (argc < 4) { @@ -2028,7 +2044,7 @@ void run_detector(int argc, char **argv) int it_num = 100; draw_object(datacfg, cfg, weights, filename, thresh, dont_show, it_num, letter_box, benchmark_layers); } - else if (0 == strcmp(argv[2], "demo")) { + else if (0 == strcmp(argv[2], "demo") || 0 == strcmp(argv[2], "stream")) { list *options = read_data_cfg(datacfg); int classes = option_find_int(options, "classes", 20); char *name_list = option_find_str(options, "names", "data/names.list"); @@ -2036,9 +2052,18 @@ void run_detector(int argc, char **argv) if (filename) if (strlen(filename) > 0) if (filename[strlen(filename) - 1] == 0x0d) filename[strlen(filename) - 1] = 0; - demo(cfg, weights, thresh, hier_thresh, cam_index, filename, names, classes, avgframes, frame_skip, prefix, out_filename, - mjpeg_port, dontdraw_bbox, json_port, dont_show, ext_output, letter_box, time_limit_sec, http_post_host, benchmark, benchmark_layers, json_file_output); + if (0 == strcmp(argv[2], "demo")){ + demo(cfg, weights, thresh, hier_thresh, cam_index, filename, names, classes, avgframes, frame_skip, prefix, out_filename, + mjpeg_port, dontdraw_bbox, json_port, dont_show, ext_output, letter_box, time_limit_sec, http_post_host, benchmark, benchmark_layers, json_file_output); + } +#ifdef STREAM + if (0 == strcmp(argv[2], "stream")){ + stream(cfg, weights, thresh, hier_thresh, cam_index, filename, names, classes, avgframes, frame_skip, prefix, out_filename, + mjpeg_port, dontdraw_bbox, json_port, dont_show, ext_output, letter_box, time_limit_sec, http_post_host, benchmark, benchmark_layers, + stream_bitrate, stream_addr, stream_frame_width, stream_frame_height, stream_profile, stream_gop_size, stream_fps); + } +#endif free_list_contents_kvp(options); free_list(options); } diff --git a/src/image_ffmpeg.cpp b/src/image_ffmpeg.cpp new file mode 100644 index 00000000000..03f33dcf5eb --- /dev/null +++ b/src/image_ffmpeg.cpp @@ -0,0 +1,240 @@ +#include "image.h" +#include +#include +#include "darknet.h" + +#include "image_opencv.h" +#include "image_ffmpeg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "libavcodec/avcodec.h" +#include "libavdevice/avdevice.h" +#include "libavformat/avformat.h" +#include "libavfilter/avfilter.h" +#include "libavutil/avutil.h" +#include "libavutil/time.h" +#include "libswscale/swscale.h" +#include "libavutil/pixdesc.h" + +#ifdef __cplusplus +} +#endif + +#pragma comment(lib, "avformat.lib") +#pragma comment(lib, "avdevice.lib") +#pragma comment(lib, "avcodec.lib") +#pragma comment(lib, "avutil.lib") +#pragma comment(lib, "avfilter.lib") +#pragma comment(lib, "swscale.lib") +#pragma comment(lib, "swresample.lib") +#pragma comment(lib, "postproc.lib") + +using namespace std; +using namespace cv; + +image mat_to_image(cv::Mat mat) +{ + int w = mat.cols; + int h = mat.rows; + int c = mat.channels(); + image im = make_image(w, h, c); + unsigned char *data = (unsigned char *)mat.data; + int step = mat.step; + for (int y = 0; y < h; ++y) { + for (int k = 0; k < c; ++k) { + for (int x = 0; x < w; ++x) { + //uint8_t val = mat.ptr(y)[c * x + k]; + //uint8_t val = mat.at(y, x).val[k]; + //im.data[k*w*h + y*w + x] = val / 255.0f; + im.data[k*w*h + y*w + x] = data[y*step + x*c + k] / 255.0f; + } + } + } + return im; +} + +#ifdef __cplusplus +extern "C" { +#endif + +bool nRestart = false; +AVFormatContext *ifmt_ctx = NULL; +AVStream *pVst; +AVCodecContext *pVideoCodecCtx = NULL; +AVCodec *pVideoCodec = NULL; +IplImage *pRgbImg; +AVFrame *pFrame = av_frame_alloc(); +int got_picture; +//uint8_t* buffer_rgb = NULL; +AVFrame *pFrameRGB = av_frame_alloc(); +SwsContext *img_convert_ctx = NULL; +//cv::Mat Img; +AVDictionary *optionsDict = NULL; +char errbuf[64]; +unsigned int i; +AVStream *st = NULL; +AVPacket pkt; +int video_st_index = -1; +int audio_st_index = -1; +int ret; + +#ifdef FFMPEG +void close_stream() +{ + if (NULL != ifmt_ctx) { + avformat_close_input(&ifmt_ctx); + ifmt_ctx = NULL; + } + exit(0); +} + +extern "C" void av_pkt_unref() +{ + av_packet_unref(&pkt); +} + +extern "C" void open_video_stream(const char *filename) +{ + av_register_all(); // Register all codecs and formats so that they can be used. + avformat_network_init(); // Initialization of network components + av_dict_set(&optionsDict, "rtsp_transport", "tcp", 0); //采用tcp传输 ,,如果不设置这个有些rtsp流就会卡着 + av_dict_set(&optionsDict, "stimeout", "2000000", 0); //如果没有设置stimeout + + av_init_packet(&pkt); // initialize packet. + pkt.data = NULL; + pkt.size = 0; + + if ((ret = avformat_open_input(&ifmt_ctx, filename, 0, &optionsDict)) < 0) { // Open the input file for reading. + printf("Could not open input file '%s' (error '%s')\n", filename, av_make_error_string(errbuf, sizeof(errbuf), ret)); + close_stream(); + } + + if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) { // Get information on the input file (number of streams etc.). + printf("Could not open find stream info (error '%s')\n", av_make_error_string(errbuf, sizeof(errbuf), ret)); + close_stream(); + } + + for (i = 0; i < ifmt_ctx->nb_streams; i++) { // dump information + av_dump_format(ifmt_ctx, i, filename, 0); + } + + for (i = 0; i < ifmt_ctx->nb_streams; i++) { // find video stream index + st = ifmt_ctx->streams[i]; + switch (st->codec->codec_type) { + case AVMEDIA_TYPE_AUDIO: audio_st_index = i; break; + case AVMEDIA_TYPE_VIDEO: video_st_index = i; break; + default: break; + } + } + if (-1 == video_st_index) { + printf("No H.264 video stream in the input file\n"); + close_stream(); + } +} + +extern "C" image get_image_from_ffmpeg_stream_resize(mat_cv** in_image, int w, int h, int c) +{ + cv::Mat *mat = NULL; + image empty_im = make_empty_image(0,0,0); + + //cv::Mat Img; + + do{ + ret = av_read_frame(ifmt_ctx, &pkt); // read frames + }while(ret == AVERROR(EAGAIN) || pkt.stream_index != video_st_index); + + if (ret < 0) { + printf("Could not read frame ---(error '%s')\n", av_make_error_string(errbuf, sizeof(errbuf), ret)); + close_stream(); + } + + if (pkt.stream_index == video_st_index) { // video frame + printf("Video Packet size = %d\n", pkt.size); + } + else if (pkt.stream_index == audio_st_index) { // audio frame + printf("Audio Packet size = %d\n", pkt.size); + } + else { + printf("Unknow Packet size = %d\n", pkt.size); + } + + //decode stream + if (!nRestart) + { + pVst = ifmt_ctx->streams[video_st_index]; + pVideoCodecCtx = pVst->codec; + pVideoCodec = avcodec_find_decoder(pVideoCodecCtx->codec_id); + if (pVideoCodec == NULL) + return empty_im; + //pVideoCodecCtx = avcodec_alloc_context3(pVideoCodec); + + if (avcodec_open2(pVideoCodecCtx, pVideoCodec, NULL) < 0) + return empty_im; + nRestart = true; + } + + if (pkt.stream_index == video_st_index) + { + fprintf(stdout, "pkt.size=%d,pkt.pts=%lld, pkt.data=0x%x.\n", pkt.size, pkt.pts, (unsigned int)pkt.data); + int av_result = avcodec_decode_video2(pVideoCodecCtx, pFrame, &got_picture, &pkt); + + if (got_picture) + { + fprintf(stdout, "decode one video frame!\n"); + } + + if (av_result < 0) + { + fprintf(stderr, "decode failed: inputbuf = 0x%x , input_framesize = %d\n", pkt.data, pkt.size); + return empty_im; + } + if (got_picture) + { + int bytes = avpicture_get_size(AV_PIX_FMT_RGB24, pVideoCodecCtx->width, pVideoCodecCtx->height); + uint8_t *buffer_rgb = (uint8_t *)av_malloc(bytes); + avpicture_fill((AVPicture *)pFrameRGB, buffer_rgb, AV_PIX_FMT_RGB24, pVideoCodecCtx->width, pVideoCodecCtx->height); + + img_convert_ctx = sws_getContext(pVideoCodecCtx->width, pVideoCodecCtx->height, pVideoCodecCtx->pix_fmt, + pVideoCodecCtx->width, pVideoCodecCtx->height, AV_PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL); + if (img_convert_ctx == NULL) + { + + printf("can't init convert context!\n"); + return empty_im; + } + sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pVideoCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); + pRgbImg = cvCreateImage(cvSize(pVideoCodecCtx->width, pVideoCodecCtx->height), 8, 3); + + memcpy(pRgbImg->imageData, buffer_rgb, pVideoCodecCtx->width * 3 * pVideoCodecCtx->height); + //image im = ipl_to_image(pRgbImg); + + mat = new cv::Mat(); + *mat = cvarrToMat(pRgbImg); + + //Img = cvarrToMat(pRgbImg); + cvReleaseImage(&pRgbImg); + sws_freeContext(img_convert_ctx); + av_free(buffer_rgb); + } + } + + //*(cv::Mat **)in_image = &Img; + *(cv::Mat **)in_image = mat; + + cv::Mat new_img = cv::Mat(h, w, CV_8UC(c)); + cv::resize(*mat, new_img, new_img.size(), 0, 0, cv::INTER_LINEAR); + cv::cvtColor(new_img, new_img, cv::COLOR_RGB2BGR); + image im = mat_to_image(new_img); + + return im; +} + + +#endif // FFMPEG + +#ifdef __cplusplus +} +#endif diff --git a/src/image_ffmpeg.h b/src/image_ffmpeg.h new file mode 100644 index 00000000000..48e574d80f6 --- /dev/null +++ b/src/image_ffmpeg.h @@ -0,0 +1,24 @@ +#ifndef FFMPEG_H +#define FFMPEG_H + +#include "image.h" +#include "matrix.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef FFMPEG + +void close_stream(); +void av_pkt_unref(); +void open_video_stream(const char *filename); +image get_image_from_ffmpeg_stream_resize(mat_cv** in_image, int w, int h, int c); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif //FFMPEG_H diff --git a/src/stream.cpp b/src/stream.cpp new file mode 100644 index 00000000000..75ac96f1f9e --- /dev/null +++ b/src/stream.cpp @@ -0,0 +1,604 @@ +#include "network.h" +#include "detection_layer.h" +#include "region_layer.h" +#include "cost_layer.h" +#include "utils.h" +#include "parser.h" +#include "box.h" +#include "image.h" +//#include "demo.h" +#include "darknet.h" +#ifdef WIN32 +#include +#include "gettimeofday.h" +#else +#include +#endif + +#ifdef FFMPEG +#include "image_ffmpeg.h" +#endif + +#ifdef OPENCV + +#include "http_stream.h" +#include "streamer.hpp" +#include "stream.h" + +#include +#include +#include +#include +#include +#include +#include + +static char **demo_names; +static image **demo_alphabet; +static int demo_classes; + +static int nboxes = 0; +static detection *dets = NULL; + +static network net; +static image in_s ; +static image det_s; + +static cap_cv *cap; +static float fps = 0; +static float demo_thresh = 0; +static int demo_ext_output = 0; +static long long int frame_id = 0; +static int demo_json_port = -1; + + +static int avg_frames; +static int demo_index = 0; +static mat_cv** cv_images; + +mat_cv* in_img; +mat_cv* det_img; +mat_cv* show_img; + +static volatile int flag_exit; +static int letter_box = 0; + +static const int thread_wait_ms = 1; +static volatile int run_fetch_in_thread = 0; +static volatile int run_detect_in_thread = 0; + +#ifdef FFMPEG +static int input_is_stream = 0; +#endif + +using namespace streamer; +using time_point = std::chrono::high_resolution_clock::time_point; +using high_resolution_clock = std::chrono::high_resolution_clock; +using std::cerr; +using std::endl; + +class MovingAverage +{ + int size; + int pos; + bool crossed; + std::vector v; + +public: + explicit MovingAverage(int sz) + { + size = sz; + v.resize(size); + pos = 0; + crossed = false; + } + + void add_value(double value) + { + v[pos] = value; + pos++; + if(pos == size) { + pos = 0; + crossed = true; + } + } + + double get_average() + { + double avg = 0.0; + int last = crossed ? size : pos; + int k=0; + for(k=0;k min_plus_margin) { + size_t excess = streamed_frames - min_plus_margin; + double dexcess = excess; + + //add a delay ~ excess*processing_time +//#define SHOW_DELAY +#ifdef SHOW_DELAY + double delay = dexcess*avg_frame_time*1000000.0; + printf("frame %07lu adding delay %.4f\n", streamed_frames, delay); + printf("avg fps = %.2f\n", streamed_frames/elapsed); +#endif + usleep(dexcess*avg_frame_time*1000000.0); + } +} + +void process_frame(mat_cv *mat_ptr, cv::Mat &out) +{ + try{ + if (mat_ptr == NULL) return; + cv::Mat &mat = *(cv::Mat *)mat_ptr; + mat.copyTo(out); + }catch (...) { + cerr << "OpenCV exception: process_frame \n"; + } +} + +void stream_frame(Streamer &streamer, const cv::Mat &image) +{ + streamer.stream_frame(image.data); +} + + +void stream_frame(Streamer &streamer, const cv::Mat &image, int64_t frame_duration) +{ + streamer.stream_frame(image.data, frame_duration); +} + +void *fetch_in_thread(void *ptr) +{ + while (!custom_atomic_load_int(&flag_exit)) { + while (!custom_atomic_load_int(&run_fetch_in_thread)) { + if (custom_atomic_load_int(&flag_exit)) return 0; + this_thread_yield(); + } + int dont_close_stream = 0; // set 1 if your IP-camera periodically turns off and turns on video-stream + if (letter_box){ + in_s = get_image_from_stream_letterbox(cap, net.w, net.h, net.c, &in_img, dont_close_stream); + }else{ +#ifdef FFMPEG + if (input_is_stream) in_s = get_image_from_ffmpeg_stream_resize(&in_img, net.w, net.h, net.c); + else in_s = get_image_from_stream_resize(cap, net.w, net.h, net.c, &in_img, dont_close_stream); +#else + in_s = get_image_from_stream_resize(cap, net.w, net.h, net.c, &in_img, dont_close_stream); +#endif + } + if (!in_s.data) { + printf("Stream closed.\n"); + custom_atomic_store_int(&flag_exit, 1); + custom_atomic_store_int(&run_fetch_in_thread, 0); + //exit(EXIT_FAILURE); + return 0; + } + //in_s = resize_image(in, net.w, net.h); + + custom_atomic_store_int(&run_fetch_in_thread, 0); + } + return 0; +} + +void *fetch_in_thread_sync(void *ptr) +{ + custom_atomic_store_int(&run_fetch_in_thread, 1); + while (custom_atomic_load_int(&run_fetch_in_thread)) this_thread_sleep_for(thread_wait_ms); + return 0; +} + +void *detect_in_thread(void *ptr) +{ + while (!custom_atomic_load_int(&flag_exit)) { + while (!custom_atomic_load_int(&run_detect_in_thread)) { + if (custom_atomic_load_int(&flag_exit)) return 0; + this_thread_yield(); + } + + layer l = net.layers[net.n - 1]; + float *X = det_s.data; + float *prediction = network_predict(net, X); + + cv_images[demo_index] = det_img; + det_img = cv_images[(demo_index + avg_frames / 2 + 1) % avg_frames]; + demo_index = (demo_index + 1) % avg_frames; + + if (letter_box) + dets = get_network_boxes(&net, get_width_mat(in_img), get_height_mat(in_img), demo_thresh, demo_thresh, 0, 1, &nboxes, 1); // letter box + else + dets = get_network_boxes(&net, net.w, net.h, demo_thresh, demo_thresh, 0, 1, &nboxes, 0); // resized + + custom_atomic_store_int(&run_detect_in_thread, 0); + } + + return 0; +} + +void *detect_in_thread_sync(void *ptr) +{ + custom_atomic_store_int(&run_detect_in_thread, 1); + while (custom_atomic_load_int(&run_detect_in_thread)) this_thread_sleep_for(thread_wait_ms); + return 0; +} + +double get_wall_time() +{ + struct timeval walltime; + if (gettimeofday(&walltime, NULL)) { + return 0; + } + return (double)walltime.tv_sec + (double)walltime.tv_usec * .000001; +} + +void stream(char *cfgfile, char *weightfile, float thresh, float hier_thresh, int cam_index, const char *filename, char **names, int classes, int avgframes, + int frame_skip, char *prefix, char *out_filename, int mjpeg_port, int dontdraw_bbox, int json_port, int dont_show, int ext_output, int letter_box_in, int time_limit_sec, char *http_post_host, + int benchmark, int benchmark_layers, + int stream_bitrate, char *dst_stream_addr, int dst_frame_width, int dst_frame_height, char *stream_profile, int stream_gop_size, int stream_fps) +{ + if (avgframes < 1) avgframes = 1; + avg_frames = avgframes; + letter_box = letter_box_in; + in_img = det_img = show_img = NULL; + //skip = frame_skip; + image **alphabet = load_alphabet(); + int delay = frame_skip; + demo_names = names; + demo_alphabet = alphabet; + demo_classes = classes; + demo_thresh = thresh; + demo_ext_output = ext_output; + demo_json_port = json_port; + printf("Stream\n"); + net = parse_network_cfg_custom(cfgfile, 1, 1); // set batch=1 + if(weightfile){ + load_weights(&net, weightfile); + } + net.benchmark_layers = benchmark_layers; + fuse_conv_batchnorm(net); + calculate_binary_weights(net); + srand(2222222); + + if(filename){ + printf("video file: %s\n", filename); +#ifdef FFMPEG + open_video_stream(filename); + input_is_stream = 1; +#endif + cap = get_capture_video_stream(filename); + }else{ + printf("Webcam index: %d\n", cam_index); + cap = get_capture_webcam(cam_index); + } + + if (!cap) { +#ifdef WIN32 + printf("Check that you have copied file opencv_ffmpeg340_64.dll to the same directory where is darknet.exe \n"); +#endif + error("Couldn't connect to webcam.\n"); + } + + layer l = net.layers[net.n-1]; + int j; + + cv_images = (mat_cv**)xcalloc(avg_frames, sizeof(mat_cv)); + + int i; + for (i = 0; i < net.n; ++i) { + layer lc = net.layers[i]; + if (lc.type == YOLO) { + lc.mean_alpha = 1.0 / avg_frames; + l = lc; + } + } + + if (l.classes != demo_classes) { + printf("\n Parameters don't match: in cfg-file classes=%d, in data-file classes=%d \n", l.classes, demo_classes); + getchar(); + exit(0); + } + + flag_exit = 0; + + custom_thread_t fetch_thread = NULL; + custom_thread_t detect_thread = NULL; + if (custom_create_thread(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); + if (custom_create_thread(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); + + fetch_in_thread_sync(0); //fetch_in_thread(0); + det_img = in_img; + det_s = in_s; + + fetch_in_thread_sync(0); //fetch_in_thread(0); + detect_in_thread_sync(0); //fetch_in_thread(0); + det_img = in_img; + det_s = in_s; + + for (j = 0; j < avg_frames / 2; ++j) { + free_detections(dets, nboxes); + fetch_in_thread_sync(0); //fetch_in_thread(0); + detect_in_thread_sync(0); //fetch_in_thread(0); + det_img = in_img; + det_s = in_s; + } + + int count = 0; + /* + if(!prefix && !dont_show){ + int full_screen = 0; + //create_window_cv("Demo", full_screen, 1352, 1013); + } + */ + + write_cv* output_video_writer = NULL; + if (out_filename && !flag_exit) + { + int src_fps = 25; + src_fps = get_stream_fps_cpp_cv(cap); + output_video_writer = + create_video_writer(out_filename, 'D', 'I', 'V', 'X', src_fps, get_width_mat(det_img), get_height_mat(det_img), 1); + + //'H', '2', '6', '4' + //'D', 'I', 'V', 'X' + //'M', 'J', 'P', 'G' + //'M', 'P', '4', 'V' + //'M', 'P', '4', '2' + //'X', 'V', 'I', 'D' + //'W', 'M', 'V', '2' + } + + int send_http_post_once = 0; + const double start_time_lim = get_time_point(); + double before = get_time_point(); + double start_time = get_time_point(); + float avg_fps = 0; + int frame_counter = 0; + int global_frame_counter = 0; + + + Streamer streamer; + int src_frame_width = get_width_mat(det_img); + int src_frame_height = get_height_mat(det_img); + int src_fps = get_stream_fps_cpp_cv(cap); + printf("video info w = %d, h = %d, fps = %d\n", src_frame_width, src_frame_height, src_fps); + + if (!dst_stream_addr) { + fprintf(stderr, "Please input a valid stream address \n"); + exit(1); + } + + if (!dst_frame_width) dst_frame_width = src_frame_width; + if (!dst_frame_height) dst_frame_height = src_frame_height; + if (!stream_bitrate) stream_bitrate = 500000; + if (!stream_fps) stream_fps = src_fps; + if (!stream_profile) stream_profile = "high444"; + if (!stream_gop_size) stream_gop_size = 10; + + StreamerConfig streamer_config(src_frame_width, src_frame_height, + dst_frame_width, dst_frame_height, + stream_fps, stream_bitrate, stream_gop_size, stream_profile, dst_stream_addr); + + streamer.enable_av_debug_log(); + streamer.init(streamer_config); + printf("stream info w = %d, h = %d, fps = %d, bitrate = %d, profile = %s, gop = %d, address = %s\n", dst_frame_width, dst_frame_height, stream_fps, stream_bitrate, stream_profile, stream_gop_size, dst_stream_addr); + + size_t streamed_frames = 0; + + high_resolution_clock clk; + time_point time_start = clk.now(); + time_point time_prev = time_start; + + MovingAverage moving_average(10); + double avg_frame_time; + + cv::Mat proc_frame; + + time_point time_stop = clk.now(); + auto elapsed_time = std::chrono::duration_cast>(time_stop - time_start); + auto frame_time = std::chrono::duration_cast>(time_stop - time_prev); + + while(1){ + ++count; + { + const float nms = .45; // 0.4F + int local_nboxes = nboxes; + detection *local_dets = dets; + this_thread_yield(); + + if (!benchmark) custom_atomic_store_int(&run_fetch_in_thread, 1); // if (custom_create_thread(&fetch_thread, 0, fetch_in_thread, 0)) error("Thread creation failed"); + custom_atomic_store_int(&run_detect_in_thread, 1); // if (custom_create_thread(&detect_thread, 0, detect_in_thread, 0)) error("Thread creation failed"); + + //if (nms) do_nms_obj(local_dets, local_nboxes, l.classes, nms); // bad results + if (nms) { + if (l.nms_kind == DEFAULT_NMS) do_nms_sort(local_dets, local_nboxes, l.classes, nms); + else diounms_sort(local_dets, local_nboxes, l.classes, nms, l.nms_kind, l.beta_nms); + } + + if (l.embedding_size) set_track_id(local_dets, local_nboxes, demo_thresh, l.sim_thresh, l.track_ciou_norm, l.track_history_size, l.dets_for_track, l.dets_for_show); + + //printf("\033[2J"); + //printf("\033[1;1H"); + //printf("\nFPS:%.1f\n", fps); + printf("Objects:\n\n"); + + ++frame_id; + if (demo_json_port > 0) { + int timeout = 400000; + send_json(local_dets, local_nboxes, l.classes, demo_names, frame_id, demo_json_port, timeout); + } + + //char *http_post_server = "webhook.site/898bbd9b-0ddd-49cf-b81d-1f56be98d870"; + if (http_post_host && !send_http_post_once) { + int timeout = 3; // 3 seconds + int http_post_port = 80; // 443 https, 80 http + if (send_http_post_request(http_post_host, http_post_port, filename, + local_dets, nboxes, classes, names, frame_id, ext_output, timeout)) + { + if (time_limit_sec > 0) send_http_post_once = 1; + } + } + + if (!benchmark && !dontdraw_bbox) draw_detections_cv_v3(show_img, local_dets, local_nboxes, demo_thresh, demo_names, demo_alphabet, demo_classes, demo_ext_output); + free_detections(local_dets, local_nboxes); + + printf("\nFPS:%.1f \t AVG_FPS:%.1f\n", fps, avg_fps); + + if(!prefix){ + if (!dont_show) { + const int each_frame = max_val_cmp(1, avg_fps / 60); + if(global_frame_counter % each_frame == 0){ + //show_image_mat(show_img, "Demo"); + process_frame(show_img, proc_frame); + if(!filename){ + stream_frame(streamer, proc_frame); + }else{ + stream_frame(streamer, proc_frame, frame_time.count()*streamer.inv_stream_timebase); + } + } + int c = wait_key_cv(1); + if (c == 10) { + if (frame_skip == 0) frame_skip = 60; + else if (frame_skip == 4) frame_skip = 0; + else if (frame_skip == 60) frame_skip = 4; + else frame_skip = 0; + } + else if (c == 27 || c == 1048603) // ESC - exit (OpenCV 2.x / 3.x) + { + flag_exit = 1; + } + } + }else{ + char buff[256]; + sprintf(buff, "%s_%08d.jpg", prefix, count); + if(show_img) save_cv_jpg(show_img, buff); + } + + // if you run it with param -mjpeg_port 8090 then open URL in your web-browser: http://localhost:8090 + if (mjpeg_port > 0 && show_img) { + int port = mjpeg_port; + int timeout = 400000; + int jpeg_quality = 40; // 1 - 100 + send_mjpeg(show_img, port, timeout, jpeg_quality); + } + + // save video file + if (output_video_writer && show_img) { + write_frame_cv(output_video_writer, show_img); + printf("\n cvWriteFrame \n"); + } + + while (custom_atomic_load_int(&run_detect_in_thread)) { + if(avg_fps > 180) this_thread_yield(); + else this_thread_sleep_for(thread_wait_ms); // custom_join(detect_thread, 0); + } + if (!benchmark) { + while (custom_atomic_load_int(&run_fetch_in_thread)) { + if (avg_fps > 180) this_thread_yield(); + else this_thread_sleep_for(thread_wait_ms); // custom_join(fetch_thread, 0); + } + free_image(det_s); + } + + if (time_limit_sec > 0 && (get_time_point() - start_time_lim)/1000000 > time_limit_sec) { + printf(" start_time_lim = %f, get_time_point() = %f, time spent = %f \n", start_time_lim, get_time_point(), get_time_point() - start_time_lim); + break; + } + + if (flag_exit == 1) break; + + if(delay == 0){ + if(!benchmark) release_mat(&show_img); + show_img = det_img; + } + det_img = in_img; + det_s = in_s; + } + --delay; + if(delay < 0){ + delay = frame_skip; + + //double after = get_wall_time(); + //float curr = 1./(after - before); + double after = get_time_point(); // more accurate time measurements + float curr = 1000000. / (after - before); + fps = fps*0.9 + curr*0.1; + before = after; + + float spent_time = (get_time_point() - start_time) / 1000000; + frame_counter++; + global_frame_counter++; + if (spent_time >= 3.0f) { + //printf(" spent_time = %f \n", spent_time); + avg_fps = frame_counter / spent_time; + frame_counter = 0; + start_time = get_time_point(); + } + } +#ifdef FFMPEG + av_pkt_unref(); +#endif + time_stop = clk.now(); + elapsed_time = std::chrono::duration_cast>(time_stop - time_start); + frame_time = std::chrono::duration_cast>(time_stop - time_prev); + + streamed_frames++; + moving_average.add_value(frame_time.count()); + avg_frame_time = moving_average.get_average(); + //add_delay(streamed_frames, stream_fps, elapsed_time.count(), avg_frame_time); + + //ok = video_capture.read(read_frame); + time_prev = time_stop; + } + printf("input video stream closed. \n"); + if (output_video_writer) { + release_video_writer(&output_video_writer); + printf("output_video_writer closed. \n"); + } + + this_thread_sleep_for(thread_wait_ms); + + custom_join(detect_thread, 0); + custom_join(fetch_thread, 0); + + // free memory + free_image(in_s); + free_detections(dets, nboxes); + + demo_index = (avg_frames + demo_index - 1) % avg_frames; + for (j = 0; j < avg_frames; ++j) { + release_mat(&cv_images[j]); + } + free(cv_images); + + free_ptrs((void **)names, net.layers[net.n - 1].classes); + + const int nsize = 8; + for (j = 0; j < nsize; ++j) { + for (i = 32; i < 127; ++i) { + free_image(alphabet[j][i]); + } + free(alphabet[j]); + } + free(alphabet); + free_network(net); + //cudaProfilerStop(); +} +#else +void stream(char *cfgfile, char *weightfile, float thresh, float hier_thresh, int cam_index, const char *filename, char **names, int classes, int avgframes, + int frame_skip, char *prefix, char *out_filename, int mjpeg_port, int dontdraw_bbox, int json_port, int dont_show, int ext_output, int letter_box_in, int time_limit_sec, char *http_post_host, + int benchmark, int benchmark_layers, + int stream_bitrate, char *dst_stream_addr, int dst_frame_width, int dst_frame_height, char *stream_profile, int stream_gop_size, int stream_fps) +{ + fprintf(stderr, "Stream needs OpenCV for webcam images.\n"); +} +#endif diff --git a/src/stream.h b/src/stream.h new file mode 100644 index 00000000000..df186f94d07 --- /dev/null +++ b/src/stream.h @@ -0,0 +1,18 @@ +#ifndef STREAM_H +#define STREAM_H + +#include "image.h" +#ifdef __cplusplus +extern "C" { +#endif + +void stream(char *cfgfile, char *weightfile, float thresh, float hier_thresh, int cam_index, const char *filename, char **names, int classes, int avgframes, + int frame_skip, char *prefix, char *out_filename, int mjpeg_port, int dontdraw_bbox, int json_port, int dont_show, int ext_output, int letter_box_in, int time_limit_sec, char *http_post_host, + int benchmark, int benchmark_layers, + int stream_bitrate, char *dst_stream_addr, int dst_frame_width, int dst_frame_height, char *stream_profile, int stream_gop_size, int stream_fps); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/streamer.cpp b/src/streamer.cpp new file mode 100644 index 00000000000..0b4c9ffe5dc --- /dev/null +++ b/src/streamer.cpp @@ -0,0 +1,263 @@ +//from https://github.com/andreanobile/opencv_ffmpeg_streaming/ +#include "streamer.hpp" + +#include +#include +#include +#include +#include + +#define __STDC_CONSTANT_MACROS +#ifdef _WIN32 +//Windows +extern "C" +{ +#include "libavformat/avformat.h" +#include "libavutil/mathematics.h" +#include "libavutil/time.h" +}; +#else +//Linux... +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#include +#include +#ifdef __cplusplus +}; +#endif +#endif + + +namespace streamer +{ + +#define STREAM_PIX_FMT AV_PIX_FMT_YUV420P + +static int encode_and_write_frame(AVCodecContext *codec_ctx, AVFormatContext *fmt_ctx, AVFrame *frame) +{ + AVPacket pkt = {0}; + av_init_packet(&pkt); + + int ret = avcodec_send_frame(codec_ctx, frame); + if (ret < 0) + { + fprintf(stderr, "Error sending frame to codec context!\n"); + return ret; + } + + ret = avcodec_receive_packet(codec_ctx, &pkt); + if (ret < 0) + { + fprintf(stderr, "Error receiving packet from codec context!\n" ); + return ret; + } + + av_interleaved_write_frame(fmt_ctx, &pkt); + av_packet_unref(&pkt); + + return 0; +} + + +static int set_options_and_open_encoder(AVFormatContext *fctx, AVStream *stream, AVCodecContext *codec_ctx, AVCodec *codec, + std::string codec_profile, double width, double height, + int fps, int bitrate, int gop_size, AVCodecID codec_id) +{ + const AVRational dst_fps = {fps, 1}; + + codec_ctx->codec_tag = 0; + codec_ctx->codec_id = codec_id; + codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; + codec_ctx->width = width; + codec_ctx->height = height; + //codec_ctx->gop_size = 12; + codec_ctx->gop_size = gop_size; + codec_ctx->pix_fmt = STREAM_PIX_FMT; + codec_ctx->framerate = dst_fps; + codec_ctx->time_base = av_inv_q(dst_fps); + codec_ctx->bit_rate = bitrate; + if (fctx->oformat->flags & AVFMT_GLOBALHEADER) + { + codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + } + + stream->time_base = codec_ctx->time_base; //will be set afterwards by avformat_write_header to 1/1000 + + int ret = avcodec_parameters_from_context(stream->codecpar, codec_ctx); + if (ret < 0) + { + fprintf(stderr, "Could not initialize stream codec parameters!\n"); + return 1; + } + + AVDictionary *codec_options = nullptr; + av_dict_set(&codec_options, "profile", codec_profile.c_str(), 0); + av_dict_set(&codec_options, "preset", "ultrafast", 0); + av_dict_set(&codec_options, "tune", "zerolatency", 0); + av_dict_set(&codec_options, "crf", "30", 0); + av_dict_set(&codec_options, "g", "1", 0); + av_dict_set(&codec_options, "ar", "44100", 0); + av_dict_set(&codec_options, "strict", "-2", 0); + av_dict_set(&codec_options, "ac", "1", 0); + av_dict_set(&codec_options, "q", "10", 0); + + // open video encoder + ret = avcodec_open2(codec_ctx, codec, &codec_options); + if (ret < 0) + { + fprintf(stderr, "Could not open video encoder!\n"); + return 1; + } + av_dict_free(&codec_options); + return 0; +} + + +Streamer::Streamer() +{ + format_ctx = nullptr; + out_codec = nullptr; + out_stream = nullptr; + out_codec_ctx = nullptr; + rtmp_server_conn = false; + av_register_all(); + inv_stream_timebase = 30.0; + network_init_ok = !avformat_network_init(); +} + + +void Streamer::cleanup() +{ + if(out_codec_ctx) { + avcodec_close(out_codec_ctx); + avcodec_free_context(&out_codec_ctx); + } + + if(format_ctx) { + if(format_ctx->pb) { + avio_close(format_ctx->pb); + } + avformat_free_context(format_ctx); + format_ctx = nullptr; + } +} + + +Streamer::~Streamer() +{ + cleanup(); + avformat_network_deinit(); +} + + + +void Streamer::stream_frame(const uint8_t *data) +{ + if(can_stream()) { + const int stride[] = {static_cast(config.src_width*3)}; + sws_scale(scaler.ctx, &data, stride, 0, config.src_height, picture.frame->data, picture.frame->linesize); + picture.frame->pts += av_rescale_q(1, out_codec_ctx->time_base, out_stream->time_base); + encode_and_write_frame(out_codec_ctx, format_ctx, picture.frame); + } +} + + +void Streamer::stream_frame(const uint8_t *data, int64_t frame_duration) +{ + if(can_stream()) { + const int stride[] = {static_cast(config.src_width*3)}; + sws_scale(scaler.ctx, &data, stride, 0, config.src_height, picture.frame->data, picture.frame->linesize); + picture.frame->pts += frame_duration; //time of frame in milliseconds + encode_and_write_frame(out_codec_ctx, format_ctx, picture.frame); + } +} + + +void Streamer::enable_av_debug_log() +{ + //av_log_set_level(AV_LOG_DEBUG); + //av_log_set_level(AV_LOG_QUIET); + //av_log_set_level(AV_LOG_INFO); + av_log_set_level(AV_LOG_VERBOSE); + //av_log_set_level(AV_LOG_MAX_OFFSET); + //av_log_set_level(AV_LOG_TRACE); +} + + +int Streamer::init(const StreamerConfig &streamer_config) +{ + init_ok = false; + cleanup(); + + config = streamer_config; + + if(!network_init_ok) { + return 1; + } + + //initialize format context for output with flv and no filename + avformat_alloc_output_context2(&format_ctx, nullptr, "flv", nullptr); + if(!format_ctx) { + return 1; + } + + //AVIOContext for accessing the resource indicated by url + if (!(format_ctx->oformat->flags & AVFMT_NOFILE)) { + int avopen_ret = avio_open2(&format_ctx->pb, config.server.c_str(), + AVIO_FLAG_WRITE, nullptr, nullptr); + if (avopen_ret < 0) { + fprintf(stderr, "failed to open stream output context, stream will not work\n"); + return 1; + } + rtmp_server_conn = true; + } + + //use selected codec + AVCodecID codec_id = AV_CODEC_ID_H264; + out_codec = avcodec_find_encoder(codec_id); + if (!(out_codec)) { + fprintf(stderr, "Could not find encoder for '%s'\n", + avcodec_get_name(codec_id)); + return 1; + } + + out_stream = avformat_new_stream(format_ctx, out_codec); + if (!out_stream) { + fprintf(stderr, "Could not allocate stream\n"); + return 1; + } + + out_codec_ctx = avcodec_alloc_context3(out_codec); + + if(set_options_and_open_encoder(format_ctx, out_stream, out_codec_ctx, out_codec, config.profile, + config.dst_width, config.dst_height, config.fps, config.bitrate, config.gop_size, codec_id)) { + return 1; + } + + out_stream->codecpar->extradata_size = out_codec_ctx->extradata_size; + out_stream->codecpar->extradata = static_cast(av_mallocz(out_codec_ctx->extradata_size)); + memcpy(out_stream->codecpar->extradata, out_codec_ctx->extradata, out_codec_ctx->extradata_size); + + av_dump_format(format_ctx, 0, config.server.c_str(), 1); + + picture.init(out_codec_ctx->pix_fmt, config.dst_width, config.dst_height); + scaler.init(out_codec_ctx, config.src_width, config.src_height,config.dst_width, config.dst_height, SWS_BILINEAR); + + if (avformat_write_header(format_ctx, nullptr) < 0) + { + fprintf(stderr, "Could not write header!\n"); + return 1; + } + + printf("stream time base = %d / %d \n", out_stream->time_base.num, out_stream->time_base.den); + + inv_stream_timebase = (double)out_stream->time_base.den/(double)out_stream->time_base.num; + + init_ok = true; + return 0; +} + +} // namespace streamer diff --git a/src/streamer.hpp b/src/streamer.hpp new file mode 100644 index 00000000000..377cbd193d7 --- /dev/null +++ b/src/streamer.hpp @@ -0,0 +1,200 @@ +// from https://github.com/andreanobile/opencv_ffmpeg_streaming +#ifndef STREAMER_HPP +#define STREAMER_HPP + +#ifdef _WIN32 +//Windows +extern "C" { +#include "libavutil/opt.h" +#include "libavcodec/avcodec.h" +#include "libavutil/channel_layout.h" +#include "libavutil/common.h" +#include "libavutil/imgutils.h" +#include "libavutil/mathematics.h" +#include "libavutil/samplefmt.h" + +#include "libavformat/avformat.h" +#include "libavcodec/avcodec.h" +#include "libavutil/imgutils.h" +#include "libswscale/swscale.h" +}; +#else +//Linux... +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef __cplusplus +}; +#endif +#endif + +#include + + +namespace streamer +{ + + +class Scaler +{ +public: + SwsContext *ctx; + + Scaler() + { + ctx = nullptr; + } + + ~Scaler() + { + if(ctx) { + sws_freeContext(ctx); + } + } + + int init(AVCodecContext *codec_ctx, int src_width, int src_height, int dst_width, int dst_height, int flags) + { + ctx = sws_getContext(src_width, src_height, AV_PIX_FMT_BGR24, dst_width, dst_height, + codec_ctx->pix_fmt, flags, nullptr, nullptr, nullptr); + if(!ctx) { + fprintf(stderr, "Could not initialize sample scaler!\n"); + return 1; + } + return 0; + } +}; + + + +class Picture +{ + static const int align_frame_buffer = 32; +public: + + AVFrame *frame; + uint8_t *data; + + int init(enum AVPixelFormat pix_fmt, int width, int height) + { + frame = nullptr; + data = nullptr; + frame = av_frame_alloc(); + + int sz = av_image_get_buffer_size(pix_fmt, width, height, align_frame_buffer); + int ret = posix_memalign(reinterpret_cast(&data), align_frame_buffer, sz); + + av_image_fill_arrays(frame->data, frame->linesize, data, pix_fmt, width, height, align_frame_buffer); + frame->format = pix_fmt; + frame->width = width; + frame->height = height; + + return ret; + } + + Picture() + { + frame = nullptr; + data = nullptr; + } + + + ~Picture() + { + if(data) { + free(data); + data = nullptr; + } + + if(frame) { + av_frame_free(&frame); + } + } +}; + + +struct StreamerConfig +{ + int src_width; + int src_height; + int dst_width; + int dst_height; + int fps; + int bitrate; + int gop_size; + std::string profile; + std::string server; + + StreamerConfig() + { + dst_width = 0; + dst_height = 0; + src_width = 0; + src_height = 0; + fps = 0; + bitrate = 0; + gop_size = 12; + } + + StreamerConfig(int source_width, int source_height, int stream_width, int stream_height, int stream_fps, int stream_bitrate, int stream_gop_size, + const std::string &stream_profile, + const std::string &stream_server) + { + src_width = source_width; + src_height = source_height; + dst_width = stream_width; + dst_height = stream_height; + fps = stream_fps; + bitrate = stream_bitrate; + gop_size = stream_gop_size; + profile = stream_profile; + server = stream_server; + } +}; + + +class Streamer +{ + bool network_init_ok; + bool rtmp_server_conn; + bool init_ok; + + AVFormatContext *format_ctx; + AVCodec *out_codec; + AVStream *out_stream; + AVCodecContext *out_codec_ctx; + + Scaler scaler; + Picture picture; + + void cleanup(); + bool can_stream() + { + return network_init_ok && rtmp_server_conn && init_ok; + } + +public: + double inv_stream_timebase; + StreamerConfig config; + Streamer(); + ~Streamer(); + void enable_av_debug_log(); + int init(const StreamerConfig &streamer_config); + void stream_frame(const uint8_t *data); + void stream_frame(const uint8_t *data, int64_t frame_duration); + +}; + +} // namespace streamer +#endif