├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── Modules │ ├── FindHiSiliconSDK.cmake │ └── get_hisisdk_ver.py ├── mini.ini ├── src ├── compat.h ├── config │ ├── app_config.c │ ├── app_config.h │ ├── config.c │ ├── config.h │ ├── sensor_config.c │ └── sensor_config.h ├── gpio.c ├── gpio.h ├── hierrors.c ├── hierrors.h ├── http_post.c ├── http_post.h ├── jpeg.c ├── jpeg.h ├── main.c ├── mmap.h ├── motion_detect.c ├── motion_detect.h ├── mp4 │ ├── bitbuf.c │ ├── bitbuf.h │ ├── moof.c │ ├── moof.h │ ├── moov.c │ ├── moov.h │ ├── mp4.c │ ├── mp4.h │ ├── nal.c │ └── nal.h ├── night.c ├── night.h ├── rtsp │ ├── ringfifo.c │ ├── ringfifo.h │ ├── rtputils.c │ ├── rtputils.h │ ├── rtspservice.c │ ├── rtspservice.h │ ├── rtsputils.c │ └── rtsputils.h ├── sensor.c ├── sensor.h ├── server.c ├── server.h ├── stack.c ├── tools.c ├── tools.h ├── videohw.c └── videohw.h └── tools └── cmake └── toolchains └── arm-openipc-linux-musleabi.cmake /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | AlignAfterOpenBracket: AlwaysBreak 5 | AlignConsecutiveMacros: true 6 | StatementMacros: ["vfunc", "vfuncDefault", "AUDIO_FORMATS"] 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .cache 3 | .clangd 4 | compile_commands.json 5 | lib 6 | output 7 | rebuild* 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "glutinium"] 2 | path = glutinium 3 | url = https://github.com/ZigFisher/glutinium.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | project(mini C) 3 | 4 | set(CMAKE_C_STANDARD 99) 5 | 6 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") 7 | 8 | find_package(HiSiliconSDK REQUIRED) 9 | find_package(Threads REQUIRED) 10 | 11 | include_directories( 12 | src 13 | ${VENDOR_SDK_INCLUDE_DIRS} 14 | ) 15 | 16 | add_executable(${PROJECT_NAME} 17 | src/mp4/bitbuf.c 18 | src/mp4/bitbuf.h 19 | src/mp4/moof.c 20 | src/mp4/moof.h 21 | src/mp4/moov.c 22 | src/mp4/moov.h 23 | src/mp4/mp4.c 24 | src/mp4/mp4.h 25 | src/mp4/nal.c 26 | src/mp4/nal.h 27 | 28 | src/config/app_config.c 29 | src/config/app_config.h 30 | src/config/config.c 31 | src/config/config.h 32 | src/config/sensor_config.c 33 | src/config/sensor_config.h 34 | 35 | src/rtsp/ringfifo.c 36 | src/rtsp/ringfifo.h 37 | src/rtsp/rtputils.c 38 | src/rtsp/rtputils.h 39 | src/rtsp/rtspservice.c 40 | src/rtsp/rtspservice.h 41 | src/rtsp/rtsputils.c 42 | src/rtsp/rtsputils.h 43 | 44 | src/compat.h 45 | src/gpio.c 46 | src/gpio.h 47 | src/hierrors.c 48 | src/hierrors.h 49 | src/http_post.c 50 | src/http_post.h 51 | src/jpeg.c 52 | src/jpeg.h 53 | src/main.c 54 | src/mmap.h 55 | src/motion_detect.c 56 | src/motion_detect.h 57 | src/night.c 58 | src/night.h 59 | src/sensor.c 60 | src/sensor.h 61 | src/server.c 62 | src/server.h 63 | src/stack.c 64 | src/tools.c 65 | src/tools.h 66 | src/videohw.c 67 | src/videohw.h 68 | ) 69 | 70 | target_link_libraries(${PROJECT_NAME} 71 | ${HILIBS} 72 | ${VENDOR_SDK_LIBRARIES} 73 | Threads::Threads 74 | dl) 75 | 76 | install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION /usr/bin) 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Mini Video Streamer 2 | =================== 3 | __A part of [OpenIPC Project](https://openipc.org/)__ 4 | 5 | ```diff 6 | @@ This project needs developers! Please contact Igor Zalatov . @@ 7 | ``` 8 | 9 | ## Description 10 | Mini is an open source video streaming software for HiSilicon IP cameras. It is a 11 | malnourished and underloved little brother of the commercial Majestic video streamer. 12 | 13 | ### Supported hardware and features 14 | 15 | | SoC Family | Audio Stream | JPEG Snapshot | RTSP Stream | Motion Detect | On-Screen Display | 16 | |-------------|:------------:|:-------------:|:-----------:|:-------------:|:-----------------:| 17 | | Hi3516CV100 | ✗ | ✗ | ✗ | ⁿ/ₐ | ✗ | 18 | | Hi3516CV200 | ✗ | ✔️ | ✔️ | ✔️ | ✗ | 19 | | Hi3516CV300 | ✗ | ✔️ | ✔️ | ✔️ | ✗ | 20 | | Hi3516CV500 | ✗ | ✗ | ✗ | ✗ | ✗ | 21 | 22 | _✔️ - supported, ✗ - not supported, ⁿ/ₐ - not supported by hardware_ 23 | 24 | ### Recommended hardware 25 | We recommend buying a [HiSilicon 3516CV300 + Sony IMX291](https://aliexpress.com/item/1005002315913099.html) 26 | board as a development kit. This IP camera module comes with 128MB of RAM and 16MB SPI Flash ROM. 27 | 28 | Use [Coupler](https://github.com/OpenIPC/coupler) to replace the stock firmware with OpenIPC. 29 | You won't even need to solder anything like a UART adapter. 30 | 31 | ### Building 32 | To clone the code locally, run 33 | ```console 34 | git clone --recurse-submodules https://github.com/openipc/mini 35 | ``` 36 | or, if you have already checked out the repository without submodules, run 37 | ```console 38 | git submodule init 39 | git submodule update 40 | ``` 41 | 42 | Build the code with CMake: 43 | ```console 44 | $ cmake -H. -Bbuild \ 45 | -DCMAKE_BUILD_TYPE=Release \ 46 | -DPLATFORM_SDK_DIR= \ 47 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ 48 | -DCMAKE_TOOLCHAIN_FILE=tools/cmake/toolchains/arm-openipc-linux-musleabi.cmake 49 | $ cmake --build build 50 | ``` 51 | Where __ is either `glutinium/hisi-osdrv2` or `glutinium/hisi-osdrv3`. 52 | 53 | ### Configuration 54 | The Mini streamer does not support sensor autodetection yet. You will need to use 55 | `ipcinfo --long_sensor` to determine the sensor model and its control bus, and then set 56 | the path to a corresponding config file as `sensor_config` parameter in `mini.ini`. 57 | 58 | ### Authors 59 | - [@widgetii](https://github.com/widgetii) 60 | -------------------------------------------------------------------------------- /cmake/Modules/FindHiSiliconSDK.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find HiSiliconSDK 2 | # Once done this will define 3 | # VENDOR_SDK_FOUND - System has HiSilicon SDK 4 | # VENDOR_SDK_INCLUDE_DIRS - The HiSilicon SDK include directories 5 | # VENDOR_SDK_LIBRARIES - The libraries needed to use HiSilicon SDK 6 | 7 | find_path(VENDOR_SDK_INCLUDE_DIRS hi_common.h 8 | HINTS ${PLATFORM_SDK_DIR} 9 | PATH_SUFFIXES mpp2/include mpp/include include source/gmp/include 10 | REQUIRED 11 | NO_CMAKE_FIND_ROOT_PATH 12 | ) 13 | 14 | execute_process(COMMAND ${CMAKE_CURRENT_LIST_DIR}/get_hisisdk_ver.py 15 | ${VENDOR_SDK_INCLUDE_DIRS}/.. 16 | OUTPUT_VARIABLE RAW_SDK_VER 17 | ) 18 | if(RAW_SDK_VER) 19 | list(GET RAW_SDK_VER 0 HISILICON_SDK_CODE) 20 | list(GET RAW_SDK_VER 1 HISILICON_SDK_FAMILY) 21 | list(GET RAW_SDK_VER 2 HISILICON_SDK_VERSION_MAJOR) 22 | list(GET RAW_SDK_VER 3 HISILICON_SDK_VERSION_MINOR) 23 | list(GET RAW_SDK_VER 4 HISILICON_SDK_VERSION_PATCH) 24 | list(GET RAW_SDK_VER 5 HISILICON_SDK_VERSION_TWEAK) 25 | list(GET RAW_SDK_VER 7 HISILICON_SDK_VERSION_DATE) 26 | list(SUBLIST RAW_SDK_VER 2 5 SDK_ONLY_VER) 27 | list(JOIN SDK_ONLY_VER "." HISILICON_SDK_VERSION) 28 | message("Detected ${HISILICON_SDK_FAMILY} SDK version ${HISILICON_SDK_VERSION}") 29 | set(VENDOR_SDK_FOUND 1) 30 | else() 31 | message(FATAL_ERROR "No HiSilicon SDK detected") 32 | endif() 33 | add_compile_definitions(HISILICON_SDK_CODE=0x${HISILICON_SDK_CODE}) 34 | add_compile_definitions(HISILICON_SDK_FAMILY="${HISILICON_SDK_FAMILY}") 35 | add_compile_definitions(HISILICON_SDK_VERSION="${HISILICON_SDK_VERSION}") 36 | 37 | if(HISILICON_SDK_CODE STREQUAL "3516C500") 38 | add_compile_definitions(MAX_VIDEO_CHANNELS=3) 39 | endif() 40 | 41 | if(HISILICON_SDK_CODE STREQUAL "7205200") 42 | set(CORE_LIBS_NAMES 43 | hi_mpi 44 | hi_md # Motion detection 45 | hi_isp # Image signal processor 46 | hi_ive # Intelligent video engine 47 | hi_ae 48 | hi_awb 49 | 50 | gk_ae 51 | gk_api 52 | gk_awb 53 | gk_awb_natura 54 | gk_bcd 55 | gk_cipher 56 | gk_isp 57 | gk_ive 58 | gk_ivp 59 | gk_md 60 | gk_qr 61 | gk_tde 62 | 63 | securec # Secure C functions 64 | upvqe # Up voice quality enhancement 65 | dnvqe # Down voice quality enhancement 66 | voice_engine 67 | ldci 68 | dehaze # Remove haze 69 | drc # Dynamic range compression 70 | ir_auto # IR Cut auto 71 | ) 72 | else() 73 | 74 | if(HISILICON_SDK_CODE STREQUAL "3518") 75 | set(SPECIFIC_LIBS 76 | resampler 77 | aec 78 | anr 79 | vqev2 80 | ) 81 | endif() 82 | 83 | set(CORE_LIBS_NAMES 84 | mpi 85 | md # Motion detection 86 | _hiae # Automatic exposure 87 | isp # Image signal processor 88 | ive # Intelligent video engine 89 | _hidehaze # Remove haze 90 | _hidefog # Remove fog 91 | _hidrc # Dynamic range compression 92 | _hildci # LDCI/Sharpen 93 | _hiawb # Automatic white balance 94 | _hiir_auto # IR Cut auto 95 | _hiaf # Automatic focus 96 | _hiacs 97 | _hicalcflicker # Flicker calculations 98 | upvqe # Up voice quality enhancement 99 | dnvqe # Down voice quality enhancement 100 | securec # Secure C functions 101 | VoiceEngine 102 | ${SPECIFIC_LIBS} 103 | ) 104 | endif() 105 | 106 | foreach(LIB ${CORE_LIBS_NAMES}) 107 | find_library(FOUND_LIB_${LIB} ${LIB} 108 | PATH_SUFFIXES mpp2/lib mpp/lib lib 109 | source/gmp/lib source/gmp/lib_log/static 110 | HINTS ${PLATFORM_SDK_DIR} 111 | NO_CMAKE_FIND_ROOT_PATH 112 | ) 113 | if(FOUND_LIB_${LIB}) 114 | list(APPEND CORE_LIBS ${FOUND_LIB_${LIB}}) 115 | endif() 116 | message("Lib: ${LIB}") 117 | message("Found Lib: ${FOUND_LIB_${LIB}}") 118 | endforeach(LIB) 119 | 120 | set(VENDOR_SDK_LIBRARIES 121 | ${CORE_LIBS} 122 | ) 123 | -------------------------------------------------------------------------------- /cmake/Modules/get_hisisdk_ver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import os 5 | import re 6 | 7 | 8 | def make_hex(string): 9 | return re.sub(r'[^\dA-Fa-f]', '', string) 10 | 11 | 12 | def make_good_name(manufacturer, hexcode): 13 | return (manufacturer + hexcode[:4] + 14 | "V" + hexcode[4:]).title() 15 | 16 | 17 | # strings ./mpp/ko/hi3516cv300_sys.ko|grep "\[SYS\] Version: " 18 | def find_sdk_version(filename, match): 19 | manufacturer = match[0][0] 20 | if manufacturer == 'hi': 21 | r = br'\[(?:SYS|ISP)\]\sVersion: \[(.+)(?:_MPP)_V(\d+)\.(\d+)\.([\d\w]+)\.(\d+) (.+ Release)*\].+\[(.+)\]' 22 | elif manufacturer == 'gk': 23 | r = br'\[ISP\]\sVersion: \[()ISP_V(\d+)\.(\d+)\.([\d\w]+)\.(\d+) (.+ Release)*\].+\[(.+)\]' 24 | else: 25 | return False 26 | pattern = re.compile(r) 27 | with open(filename, "rb") as f: 28 | ret = pattern.findall(f.read()) 29 | if ret: 30 | matches = [x.decode() for x in ret[0]] 31 | hexcode = make_hex(matches[0]) 32 | if hexcode == "": 33 | hexcode = make_hex(os.path.basename(filename)) 34 | matches[0] = make_good_name(manufacturer, hexcode) 35 | print(hexcode, ";".join(matches), sep=';', 36 | end='') 37 | return True 38 | return False 39 | 40 | 41 | # traverse root directory, and list directories as dirs and files as files 42 | def find_sys_ko(path): 43 | pattern = re.compile(r"(\w\w)[0-9a-z]+_(sys|isp).ko") 44 | for root, dirs, files in os.walk(path): 45 | path = root.split(os.sep) 46 | for file in files: 47 | match = pattern.findall(file) 48 | if match: 49 | if find_sdk_version(os.path.join(root, file), match): 50 | return 51 | 52 | 53 | def main(): 54 | if len(sys.argv) != 2: 55 | root_dir = os.getcwd() 56 | else: 57 | root_dir = sys.argv[1] 58 | find_sys_ko(root_dir) 59 | 60 | 61 | if __name__ == '__main__': 62 | main() 63 | -------------------------------------------------------------------------------- /mini.ini: -------------------------------------------------------------------------------- 1 | [system] 2 | sensor_config = /etc/sensors/jxf22_i2c_1080p.ini 3 | web_port = 80 4 | web_enable_static = false 5 | isp_thread_stack_size = 16384 # 16kb = 16*1024 6 | venc_stream_thread_stack_size = 16384 7 | web_server_thread_stack_size = 16384 8 | 9 | [isp] 10 | align_width = 64 11 | max_pool_cnt = 16 12 | blk_cnt = 5 # 5 for hi3518E, 10 for hi3516C 13 | mirror = false 14 | flip = false 15 | 16 | [night_mode] 17 | enable = false 18 | ir_sensor_pin = 62 19 | check_interval_s = 10 # interval to check light sensor state in seconds 20 | ir_cut_pin1 = 1 21 | ir_cut_pin2 = 2 22 | # switch delay in us on IRcut filter pins. WARNING! Very long delay can damage IRcut filter!!! 23 | pin_switch_delay_us = 150 24 | 25 | [record] 26 | enable = false 27 | path = /sdcard/records/ 28 | file_duration = 10 # in minutes 29 | width = 1920 30 | height = 1080 31 | fps = 25 32 | bitrate = 1024 33 | 34 | [http_post] 35 | enable = false 36 | host = 37 | # format time like C strftime call 38 | url = /~example/000000000000/%Y/%m/%d/%H.%M.jpg 39 | width = 640 40 | height = 360 41 | qfactor = 90 # [1..99] jpeg quality 42 | interval = 60 # in seconds 43 | # basic auth 44 | login = 45 | password = 46 | 47 | [mp4] 48 | enable = true 49 | width = 1920 50 | height = 1080 51 | fps = 25 52 | bitrate = 1024 # in kbits per second 53 | 54 | [jpeg] 55 | enable = true 56 | width = 1920 57 | height = 1080 58 | qfactor = 70 # [1..99] jpeg quality 59 | 60 | [mjpeg] 61 | enable = true 62 | width = 640 63 | height = 360 64 | fps = 5 65 | bitrate = 1024 # in kbits per second 66 | 67 | [rtsp] 68 | enable = true 69 | width = 1920 70 | height = 1080 71 | fps = 25 72 | bitrate = 1024 # in kbits per second 73 | udp = true 74 | tcp = true 75 | -------------------------------------------------------------------------------- /src/compat.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPAT_H 2 | #define COMPAT_H 3 | 4 | #if HISILICON_SDK_CODE == 0x3518 5 | #define HISILICON_SDK_GEN 1 6 | #elif HISILICON_SDK_CODE == 0x3518E200 7 | #define HISILICON_SDK_GEN 2 8 | #elif HISILICON_SDK_CODE == 0x3516A 9 | #define HISILICON_SDK_GEN 2 10 | #elif HISILICON_SDK_CODE == 0x3516C300 11 | #define HISILICON_SDK_GEN 3 12 | #elif HISILICON_SDK_CODE == 0x3516E200 13 | #define HISILICON_SDK_GEN 4 14 | #elif HISILICON_SDK_CODE == 0x3516C500 15 | #define HISILICON_SDK_GEN 4 16 | #elif HISILICON_SDK_CODE == 0x7205200 17 | #define HISILICON_SDK_GEN 4 18 | #endif 19 | 20 | #pragma GCC diagnostic push 21 | #pragma GCC diagnostic ignored "-Wsign-compare" 22 | #include 23 | #pragma GCC diagnostic pop 24 | 25 | #ifndef ALIGN_UP 26 | #define ALIGN_UP(x, a) ((((x) + ((a)-1)) / a) * a) 27 | #endif 28 | 29 | #include 30 | #if HISILICON_SDK_GEN >= 2 31 | #include 32 | #endif 33 | #include 34 | #if HISILICON_SDK_GEN <= 3 35 | #include 36 | #endif 37 | #if HISILICON_SDK_GEN == 4 38 | #include 39 | #include 40 | #include 41 | #endif 42 | 43 | #if HISILICON_SDK_GEN == 2 || HISILICON_SDK_GEN == 3 44 | typedef raw_data_type_e data_type_t; 45 | 46 | typedef wdr_mode_e wdr_mode_t; 47 | typedef lvds_sync_mode_e lvds_sync_mode_t; 48 | typedef lvds_bit_endian lvds_bit_endian_t; 49 | 50 | typedef BT656_FIXCODE_E VI_BT656_FIXCODE_E; 51 | typedef BT656_FIELD_POLAR_E VI_BT656_FIELD_POLAR_E; 52 | #endif 53 | 54 | #if HISILICON_SDK_GEN <= 2 55 | #define MPEG_ATTR stAttrMjpeg 56 | #define JPEG_ATTR stAttrJpeg 57 | #elif HISILICON_SDK_GEN == 3 58 | #define MPEG_ATTR stAttrMjpege 59 | #define JPEG_ATTR stAttrJpege 60 | #endif 61 | 62 | #define MIPI_DEV "/dev/hi_mipi" 63 | 64 | #endif /* COMPAT_H */ 65 | -------------------------------------------------------------------------------- /src/config/app_config.c: -------------------------------------------------------------------------------- 1 | #include "app_config.h" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct AppConfig app_config; 10 | 11 | enum ConfigError parse_app_config(const char *path) { 12 | memset(&app_config, 0, sizeof(struct AppConfig)); 13 | 14 | app_config.sensor_config[0] = 0; 15 | app_config.jpeg_enable = false; 16 | app_config.mp4_enable = false; 17 | app_config.rtsp_enable = false; 18 | app_config.osd_enable = false; 19 | app_config.motion_detect_enable = false; 20 | 21 | app_config.mjpeg_enable = false; 22 | app_config.mjpeg_fps = 15; 23 | app_config.mjpeg_width = 640; 24 | app_config.mjpeg_height = 480; 25 | app_config.mjpeg_bitrate = 1024; 26 | 27 | app_config.web_port = 8080; 28 | app_config.web_enable_static = false; 29 | 30 | app_config.isp_thread_stack_size = 16 * 1024; 31 | app_config.venc_stream_thread_stack_size = 16 * 1024; 32 | app_config.web_server_thread_stack_size = 16 * 1024; 33 | 34 | app_config.align_width = 64; 35 | app_config.blk_cnt = 4; 36 | app_config.max_pool_cnt = 16; 37 | app_config.mirror = false; 38 | app_config.flip = false; 39 | 40 | app_config.night_mode_enable = false; 41 | app_config.ir_sensor_pin = 999; 42 | app_config.ir_cut_pin1 = 999; 43 | app_config.ir_cut_pin2 = 999; 44 | app_config.pin_switch_delay_us = 250; 45 | app_config.check_interval_s = 10; 46 | 47 | struct IniConfig ini; 48 | memset(&ini, 0, sizeof(struct IniConfig)); 49 | 50 | // load config file to string 51 | ini.str = NULL; 52 | { 53 | char config_path[50]; 54 | ssize_t len = sprintf(config_path, "/etc/%s", path); 55 | FILE *file = fopen("./mini.ini", "rb"); 56 | if (!file) { 57 | file = fopen("/etc/mini.ini", "rb"); 58 | if (!file) { 59 | printf( 60 | "Can't find config mini.ini in:\n ./mini.ini\n " 61 | " /etc/mini.ini\n", 62 | path); 63 | return -1; 64 | } 65 | } 66 | 67 | fseek(file, 0, SEEK_END); 68 | size_t length = (size_t)ftell(file); 69 | fseek(file, 0, SEEK_SET); 70 | 71 | ini.str = malloc(length + 1); 72 | if (!ini.str) { 73 | printf("Can't allocate buf in parse_app_config\n"); 74 | fclose(file); 75 | return -1; 76 | } 77 | size_t n = fread(ini.str, 1, length, file); 78 | if (n != length) { 79 | printf("Can't read all file %s\n", path); 80 | fclose(file); 81 | free(ini.str); 82 | return -1; 83 | } 84 | fclose(file); 85 | ini.str[length] = 0; 86 | } 87 | 88 | enum ConfigError err; 89 | find_sections(&ini); 90 | 91 | err = parse_param_value( 92 | &ini, "system", "sensor_config", app_config.sensor_config); 93 | if (err != CONFIG_OK) 94 | goto RET_ERR; 95 | err = 96 | parse_int(&ini, "system", "web_port", 1, INT_MAX, &app_config.web_port); 97 | if (err != CONFIG_OK) 98 | goto RET_ERR; 99 | err = parse_bool( 100 | &ini, "system", "web_enable_static", &app_config.web_enable_static); 101 | if (err != CONFIG_OK) 102 | goto RET_ERR; 103 | err = parse_int( 104 | &ini, "system", "isp_thread_stack_size", 16 * 1024, INT_MAX, 105 | &app_config.isp_thread_stack_size); 106 | if (err != CONFIG_OK) 107 | goto RET_ERR; 108 | err = parse_int( 109 | &ini, "system", "venc_stream_thread_stack_size", 16 * 1024, INT_MAX, 110 | &app_config.venc_stream_thread_stack_size); 111 | if (err != CONFIG_OK) 112 | goto RET_ERR; 113 | err = parse_int( 114 | &ini, "system", "web_server_thread_stack_size", 16 * 1024, INT_MAX, 115 | &app_config.web_server_thread_stack_size); 116 | if (err != CONFIG_OK) 117 | goto RET_ERR; 118 | 119 | err = 120 | parse_bool(&ini, "night_mode", "enable", &app_config.night_mode_enable); 121 | if (err != CONFIG_OK) 122 | goto RET_ERR; 123 | if (app_config.night_mode_enable) { 124 | #define PIN_MAX 95 125 | err = parse_int( 126 | &ini, "night_mode", "ir_sensor_pin", 0, PIN_MAX, 127 | &app_config.ir_sensor_pin); 128 | if (err != CONFIG_OK) 129 | goto RET_ERR; 130 | err = parse_int( 131 | &ini, "night_mode", "check_interval_s", 0, 600, 132 | &app_config.check_interval_s); 133 | if (err != CONFIG_OK) 134 | goto RET_ERR; 135 | err = parse_int( 136 | &ini, "night_mode", "ir_cut_pin1", 0, PIN_MAX, 137 | &app_config.ir_cut_pin1); 138 | if (err != CONFIG_OK) 139 | goto RET_ERR; 140 | err = parse_int( 141 | &ini, "night_mode", "ir_cut_pin2", 0, PIN_MAX, 142 | &app_config.ir_cut_pin2); 143 | if (err != CONFIG_OK) 144 | goto RET_ERR; 145 | err = parse_int( 146 | &ini, "night_mode", "pin_switch_delay_us", 0, 1000, 147 | &app_config.pin_switch_delay_us); 148 | if (err != CONFIG_OK) 149 | goto RET_ERR; 150 | } 151 | 152 | { 153 | const char *possible_values[] = {"1", "4", "16", "64", "128"}; 154 | const int count = sizeof(possible_values) / sizeof(const char *); 155 | err = parse_enum( 156 | &ini, "isp", "align_width", &app_config.align_width, 157 | possible_values, count, 0); 158 | if (err != CONFIG_OK) 159 | goto RET_ERR; 160 | err = parse_int( 161 | &ini, "isp", "align_width", 0, INT_MAX, &app_config.align_width); 162 | if (err != CONFIG_OK) 163 | goto RET_ERR; 164 | } 165 | err = parse_int( 166 | &ini, "isp", "max_pool_cnt", 1, INT_MAX, &app_config.max_pool_cnt); 167 | if (err != CONFIG_OK) 168 | goto RET_ERR; 169 | { 170 | const char *possible_values[] = {"4", "10"}; 171 | const int count = sizeof(possible_values) / sizeof(const char *); 172 | err = parse_enum( 173 | &ini, "isp", "blk_cnt", &app_config.blk_cnt, possible_values, count, 174 | 0); 175 | if (err != CONFIG_OK) 176 | goto RET_ERR; 177 | err = 178 | parse_int(&ini, "isp", "blk_cnt", 4, INT_MAX, &app_config.blk_cnt); 179 | if (err != CONFIG_OK) 180 | goto RET_ERR; 181 | } 182 | 183 | err = parse_bool(&ini, "isp", "mirror", &app_config.mirror); 184 | if (err != CONFIG_OK) 185 | goto RET_ERR; 186 | err = parse_bool(&ini, "isp", "flip", &app_config.flip); 187 | if (err != CONFIG_OK) 188 | goto RET_ERR; 189 | 190 | err = parse_bool(&ini, "rtsp", "enable", &app_config.rtsp_enable); 191 | if (err != CONFIG_OK) 192 | goto RET_ERR; 193 | 194 | err = parse_bool(&ini, "mp4", "enable", &app_config.mp4_enable); 195 | if (err != CONFIG_OK) 196 | goto RET_ERR; 197 | if (app_config.mp4_enable) { 198 | err = parse_int( 199 | &ini, "mp4", "width", 160, INT_MAX, &app_config.mp4_width); 200 | if (err != CONFIG_OK) 201 | goto RET_ERR; 202 | err = parse_int( 203 | &ini, "mp4", "height", 120, INT_MAX, &app_config.mp4_height); 204 | if (err != CONFIG_OK) 205 | goto RET_ERR; 206 | err = parse_int(&ini, "mp4", "fps", 1, INT_MAX, &app_config.mp4_fps); 207 | if (err != CONFIG_OK) 208 | goto RET_ERR; 209 | err = parse_int( 210 | &ini, "mp4", "bitrate", 32, INT_MAX, &app_config.mp4_bitrate); 211 | if (err != CONFIG_OK) 212 | goto RET_ERR; 213 | } 214 | 215 | err = parse_bool(&ini, "jpeg", "enable", &app_config.jpeg_enable); 216 | if (err != CONFIG_OK) 217 | goto RET_ERR; 218 | if (app_config.jpeg_enable) { 219 | err = parse_int( 220 | &ini, "jpeg", "width", 160, INT_MAX, &app_config.jpeg_width); 221 | if (err != CONFIG_OK) 222 | goto RET_ERR; 223 | err = parse_int( 224 | &ini, "jpeg", "height", 120, INT_MAX, &app_config.jpeg_height); 225 | if (err != CONFIG_OK) 226 | goto RET_ERR; 227 | err = 228 | parse_int(&ini, "jpeg", "qfactor", 1, 99, &app_config.jpeg_qfactor); 229 | if (err != CONFIG_OK) 230 | goto RET_ERR; 231 | } 232 | 233 | err = parse_bool(&ini, "mjpeg", "enable", &app_config.mjpeg_enable); 234 | if (err != CONFIG_OK) 235 | goto RET_ERR; 236 | if (app_config.mjpeg_enable) { 237 | err = parse_int( 238 | &ini, "mjpeg", "width", 160, INT_MAX, &app_config.mjpeg_width); 239 | if (err != CONFIG_OK) 240 | goto RET_ERR; 241 | err = parse_int( 242 | &ini, "mjpeg", "height", 120, INT_MAX, &app_config.mjpeg_height); 243 | if (err != CONFIG_OK) 244 | goto RET_ERR; 245 | err = 246 | parse_int(&ini, "mjpeg", "fps", 1, INT_MAX, &app_config.mjpeg_fps); 247 | if (err != CONFIG_OK) 248 | goto RET_ERR; 249 | err = parse_int( 250 | &ini, "mjpeg", "bitrate", 32, INT_MAX, &app_config.mjpeg_bitrate); 251 | if (err != CONFIG_OK) 252 | goto RET_ERR; 253 | } 254 | 255 | parse_bool(&ini, "http_post", "enable", &app_config.http_post_enable); 256 | if (app_config.http_post_enable) { 257 | err = parse_param_value( 258 | &ini, "http_post", "host", app_config.http_post_host); 259 | if (err != CONFIG_OK) 260 | goto RET_ERR; 261 | err = parse_param_value( 262 | &ini, "http_post", "url", app_config.http_post_url); 263 | if (err != CONFIG_OK) 264 | goto RET_ERR; 265 | 266 | err = parse_param_value( 267 | &ini, "http_post", "login", app_config.http_post_login); 268 | if (err != CONFIG_OK) 269 | goto RET_ERR; 270 | err = parse_param_value( 271 | &ini, "http_post", "password", app_config.http_post_password); 272 | if (err != CONFIG_OK) 273 | goto RET_ERR; 274 | 275 | err = parse_int( 276 | &ini, "http_post", "width", 160, INT_MAX, 277 | &app_config.http_post_width); 278 | if (err != CONFIG_OK) 279 | goto RET_ERR; 280 | err = parse_int( 281 | &ini, "http_post", "height", 120, INT_MAX, 282 | &app_config.http_post_height); 283 | if (err != CONFIG_OK) 284 | goto RET_ERR; 285 | err = parse_int( 286 | &ini, "http_post", "interval", 1, INT_MAX, 287 | &app_config.http_post_interval); 288 | if (err != CONFIG_OK) 289 | goto RET_ERR; 290 | err = parse_int( 291 | &ini, "http_post", "qfactor", 1, 99, &app_config.http_post_qfactor); 292 | if (err != CONFIG_OK) 293 | goto RET_ERR; 294 | } 295 | 296 | free(ini.str); 297 | return CONFIG_OK; 298 | RET_ERR: 299 | free(ini.str); 300 | return err; 301 | } 302 | -------------------------------------------------------------------------------- /src/config/app_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "config.h" 3 | 4 | struct AppConfig { 5 | // [system] 6 | char sensor_config[128]; 7 | 8 | bool rtsp_enable; 9 | 10 | // [video_0] 11 | bool mp4_enable; 12 | uint32_t mp4_fps; 13 | uint32_t mp4_width; 14 | uint32_t mp4_height; 15 | uint32_t mp4_bitrate; 16 | 17 | // [jpeg] 18 | bool jpeg_enable; 19 | uint32_t jpeg_width; 20 | uint32_t jpeg_height; 21 | uint32_t jpeg_qfactor; 22 | 23 | // [mjpeg] 24 | bool mjpeg_enable; 25 | uint32_t mjpeg_fps; 26 | uint32_t mjpeg_width; 27 | uint32_t mjpeg_height; 28 | uint32_t mjpeg_bitrate; 29 | 30 | // [http_post_jpeg] 31 | bool http_post_enable; 32 | char http_post_host[128]; 33 | char http_post_url[128]; 34 | char http_post_login[128]; 35 | char http_post_password[128]; 36 | uint32_t http_post_width; 37 | uint32_t http_post_height; 38 | uint32_t http_post_qfactor; 39 | uint32_t http_post_interval; 40 | 41 | bool osd_enable; 42 | bool motion_detect_enable; 43 | 44 | uint32_t web_port; 45 | bool web_enable_static; 46 | 47 | uint32_t isp_thread_stack_size; 48 | uint32_t venc_stream_thread_stack_size; 49 | uint32_t web_server_thread_stack_size; 50 | 51 | uint32_t align_width; 52 | uint32_t max_pool_cnt; 53 | uint32_t blk_cnt; 54 | bool mirror; 55 | bool flip; 56 | 57 | // [night_mode] 58 | bool night_mode_enable; 59 | uint32_t ir_cut_pin1; 60 | uint32_t ir_cut_pin2; 61 | uint32_t ir_sensor_pin; 62 | uint32_t check_interval_s; 63 | uint32_t pin_switch_delay_us; 64 | }; 65 | 66 | extern struct AppConfig app_config; 67 | 68 | enum ConfigError parse_app_config(const char *path); 69 | -------------------------------------------------------------------------------- /src/config/config.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "../tools.h" 12 | 13 | enum ConfigError find_sections(struct IniConfig *ini) { 14 | for (unsigned int i = 0; i < MAX_SECTIONS; ++i) 15 | ini->sections[i].pos = -1; 16 | 17 | regex_t regex; 18 | if (compile_regex( 19 | ®ex, "^[[:space:]]*\\[([a-zA-Z0-9_]+)\\][[:space:]]*") < 0) { 20 | printf("compile_regex error\n"); 21 | return CONFIG_REGEX_ERROR; 22 | }; 23 | size_t n_matches = 2; // We have 1 capturing group + the whole match group 24 | regmatch_t m[n_matches]; 25 | 26 | int section_pos = 0; 27 | int section_index = 0; 28 | while (1) { 29 | if (section_index >= MAX_SECTIONS) 30 | break; 31 | int match = regexec(®ex, ini->str + section_pos, n_matches, m, 0); 32 | if (match != 0) 33 | break; 34 | int len = sprintf( 35 | ini->sections[section_index].name, "%.*s", 36 | (int)(m[1].rm_eo - m[1].rm_so), 37 | ini->str + section_pos + m[1].rm_so); 38 | ini->sections[section_index].name[len] = 0; 39 | section_pos = section_pos + (int)m[1].rm_eo; 40 | ini->sections[section_index].pos = section_pos; 41 | section_index++; 42 | } 43 | regfree(®ex); 44 | return CONFIG_OK; 45 | } 46 | 47 | enum ConfigError section_pos( 48 | struct IniConfig *ini, const char *section, int *start_pos, int *end_pos) { 49 | for (unsigned int i = 0; i < MAX_SECTIONS; ++i) { 50 | if (ini->sections[i].pos > 0 && 51 | strcmp(ini->sections[i].name, section) == 0) { 52 | *start_pos = ini->sections[i].pos; 53 | if (i + 1 < MAX_SECTIONS && ini->sections[i + i].pos > 0) 54 | *end_pos = ini->sections[i + 1].pos; 55 | else { 56 | *end_pos = -1; 57 | } 58 | return CONFIG_OK; 59 | } 60 | } 61 | return CONFIG_SECTION_NOT_FOUND; 62 | } 63 | 64 | enum ConfigError parse_param_value( 65 | struct IniConfig *ini, const char *section, const char *param_name, 66 | char *param_value) { 67 | int start_pos = 0; 68 | int end_pos = 0; 69 | if (strlen(section) > 0) { 70 | enum ConfigError err = section_pos(ini, section, &start_pos, &end_pos); 71 | if (err == CONFIG_SECTION_NOT_FOUND) { 72 | printf( 73 | "Section '%s' doesn't exists in config '%s'.\n", section, 74 | ini->path); 75 | return err; 76 | } else if (err != CONFIG_OK) 77 | return err; 78 | } 79 | 80 | regex_t regex; 81 | char reg_buf[128]; 82 | ssize_t reg_buf_len = sprintf( 83 | reg_buf, "^[[:space:]]*%s[[:space:]]*=[[:space:]]*(.[^[:space:];]*)", 84 | param_name); 85 | reg_buf[reg_buf_len] = 0; 86 | if (compile_regex(®ex, reg_buf) < 0) { 87 | printf("compile_regex error\n"); 88 | return CONFIG_REGEX_ERROR; 89 | }; 90 | size_t n_matches = 2; // We have 1 capturing group + the whole match group 91 | regmatch_t m[n_matches]; 92 | int match = regexec(®ex, ini->str + start_pos, n_matches, m, 0); 93 | regfree(®ex); 94 | if (match > 0 || (end_pos >= 0 && end_pos - start_pos < m[1].rm_so)) { 95 | printf("Can't find '%s' in section '%s'.\n", param_name, section); 96 | return CONFIG_PARAM_NOT_FOUND; 97 | } 98 | int res = sprintf( 99 | param_value, "%.*s", (int)(m[1].rm_eo - m[1].rm_so), 100 | ini->str + start_pos + m[1].rm_so); 101 | // if (res <= 0 ) { return -1; } 102 | param_value[res] = 0; 103 | return CONFIG_OK; 104 | } 105 | 106 | enum ConfigError parse_enum( 107 | struct IniConfig *ini, const char *section, const char *param_name, 108 | void *enum_value, const char *possible_values[], 109 | const int possible_values_count, const int possible_values_offset) { 110 | char param_value[64]; 111 | enum ConfigError err = 112 | parse_param_value(ini, section, param_name, param_value); 113 | if (err != CONFIG_OK) 114 | return err; 115 | 116 | // try to parse as number 117 | char *end; 118 | long res = strtol(param_value, &end, 10); 119 | if (*end) { 120 | res = strtol(param_value, &end, 16); 121 | } 122 | if (!*end) { 123 | *(VI_INPUT_MODE_E *)(enum_value) = (VI_INPUT_MODE_E)res; 124 | return CONFIG_OK; 125 | } 126 | 127 | // try to find value in possible values 128 | for (unsigned int i = 0; i < possible_values_count; ++i) 129 | if (strcmp(param_value, possible_values[i]) == 0) { 130 | *(VI_INPUT_MODE_E *)(enum_value) = 131 | (VI_INPUT_MODE_E)(possible_values_offset + i); 132 | return CONFIG_OK; 133 | } 134 | 135 | // print error 136 | printf( 137 | "Can't parse param '%s' value '%s'. Is not a number and is not in " 138 | "possible values: ", 139 | param_name, param_value); 140 | for (unsigned int i = 0; i < possible_values_count; ++i) 141 | printf("'%s', ", possible_values[i]); 142 | return CONFIG_ENUM_INCORRECT_STRING; 143 | } 144 | enum ConfigError parse_bool( 145 | struct IniConfig *ini, const char *section, const char *param_name, 146 | bool *bool_value) { 147 | const char *possible_values[] = {"0", "1", "false", "true", 148 | "n", "y", "no", "yes"}; 149 | const int count = sizeof(possible_values) / sizeof(const char *); 150 | VI_INPUT_MODE_E val; 151 | enum ConfigError err = parse_enum( 152 | ini, section, param_name, (void *)&val, possible_values, count, 0); 153 | if (err != CONFIG_OK) 154 | return err; 155 | if (val == 0 || val == 2 || val == 4 || val == 6) { 156 | *bool_value = false; 157 | return CONFIG_OK; 158 | } 159 | if (val == 1 || val == 3 || val == 5 || val == 7) { 160 | *bool_value = true; 161 | return CONFIG_OK; 162 | } 163 | *bool_value = false; 164 | return CONFIG_OK; 165 | } 166 | 167 | enum ConfigError parse_int( 168 | struct IniConfig *ini, const char *section, const char *param_name, 169 | const int min, const int max, int *int_value) { 170 | char param_value[64]; 171 | enum ConfigError err = 172 | parse_param_value(ini, section, param_name, param_value); 173 | if (err != CONFIG_OK) 174 | return err; 175 | 176 | // try to parse as number 177 | char *end = NULL; 178 | long res = strtol(param_value, &end, 10); 179 | if (*end) { 180 | res = strtol(param_value, &end, 16); 181 | } 182 | if (!*end) { 183 | if (res < min || res > max) { 184 | printf( 185 | "Can't parse param '%s' value '%s'. Value '%ld' is not in a " 186 | "range [%d; %d].", 187 | param_name, param_value, res, min, max); 188 | return CONFIG_PARAM_ISNT_IN_RANGE; 189 | } 190 | *int_value = (int)res; 191 | return CONFIG_OK; 192 | } 193 | if (strcmp(param_value, "true") == 0) { 194 | *int_value = 1; 195 | return CONFIG_OK; 196 | } 197 | if (strcmp(param_value, "TRUE") == 0) { 198 | *int_value = 1; 199 | return CONFIG_OK; 200 | } 201 | if (strcmp(param_value, "false") == 0) { 202 | *int_value = 0; 203 | return CONFIG_OK; 204 | } 205 | if (strcmp(param_value, "FALSE") == 0) { 206 | *int_value = 0; 207 | return CONFIG_OK; 208 | } 209 | 210 | printf( 211 | "Can't parse param '%s' value '%s'. Is not a integer (dec or hex) " 212 | "number.", 213 | param_name, param_value); 214 | return CONFIG_PARAM_ISNT_NUMBER; 215 | } 216 | 217 | enum ConfigError parse_array( 218 | struct IniConfig *ini, const char *section, const char *param_name, 219 | int *array, const int array_size) { 220 | char param_value[256]; 221 | enum ConfigError err = 222 | parse_param_value(ini, section, param_name, param_value); 223 | if (err != CONFIG_OK) 224 | return err; 225 | 226 | const char *token = strtok(param_value, "|"); 227 | for (int i = 0; token && i < array_size; i++) { 228 | char *end = NULL; 229 | int64_t res = strtol(token, &end, 10); 230 | if (*end) 231 | res = strtol(token, &end, 16); 232 | if (*end) { 233 | printf( 234 | "Can't parse param '%s' value '%s'. Is not a integer (dec or " 235 | "hex) number.", 236 | param_name, token); 237 | return CONFIG_PARAM_ISNT_NUMBER; 238 | } 239 | array[i] = (int)res; 240 | token = strtok(NULL, "|"); 241 | } 242 | 243 | return CONFIG_OK; 244 | } 245 | 246 | enum ConfigError parse_uint64( 247 | struct IniConfig *ini, const char *section, const char *param_name, 248 | const uint64_t min, const uint64_t max, uint64_t *int_value) { 249 | char param_value[64]; 250 | enum ConfigError err = parse_param_value( 251 | ini, section, param_name, param_value); 252 | if (err != CONFIG_OK) 253 | return err; 254 | 255 | // try to parse as number 256 | char *end = NULL; 257 | uint64_t res = strtoull(param_value, &end, 10); 258 | if (*end) { 259 | res = strtoull(param_value, &end, 16); 260 | } 261 | if (!*end) { 262 | if (res < min || res > max) { 263 | printf( 264 | "Can't parse param '%s' value '%s'. Value '%lld' is not in a " 265 | "range [%lld; %lld].\n", 266 | param_name, param_value, res, min, max); 267 | return CONFIG_PARAM_ISNT_IN_RANGE; 268 | } 269 | *int_value = (uint64_t)res; 270 | return CONFIG_OK; 271 | } 272 | if (strcmp(param_value, "true") == 0) { 273 | *int_value = 1; 274 | return CONFIG_OK; 275 | } 276 | if (strcmp(param_value, "TRUE") == 0) { 277 | *int_value = 1; 278 | return CONFIG_OK; 279 | } 280 | if (strcmp(param_value, "false") == 0) { 281 | *int_value = 0; 282 | return CONFIG_OK; 283 | } 284 | if (strcmp(param_value, "FALSE") == 0) { 285 | *int_value = 0; 286 | return CONFIG_OK; 287 | } 288 | 289 | printf( 290 | "Can't parse param '%s' value '%s'. Is not a integer (dec or hex) " 291 | "number.", 292 | param_name, param_value); 293 | return CONFIG_PARAM_ISNT_NUMBER; 294 | } 295 | 296 | 297 | enum ConfigError parse_uint32( 298 | struct IniConfig *ini, const char *section, const char *param_name, 299 | const uint32_t min, const uint32_t max, uint32_t *value) { 300 | uint64_t val = 0; 301 | enum ConfigError err = 302 | parse_uint64(ini, section, param_name, min, max, &val); 303 | if (err != CONFIG_OK) 304 | return err; 305 | *value = val; 306 | return CONFIG_OK; 307 | } 308 | -------------------------------------------------------------------------------- /src/config/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #define MAX_SECTIONS 16 6 | struct IniConfig { 7 | char path[256]; 8 | char *str; 9 | struct Section { 10 | char name[64]; 11 | int pos; 12 | } sections[MAX_SECTIONS]; 13 | }; 14 | 15 | enum ConfigError { 16 | CONFIG_OK = 0, 17 | CONFIG_SECTION_NOT_FOUND, 18 | CONFIG_PARAM_NOT_FOUND, 19 | CONFIG_PARAM_ISNT_NUMBER, 20 | CONFIG_PARAM_ISNT_IN_RANGE, 21 | CONFIG_ENUM_INCORRECT_STRING, 22 | CONFIG_REGEX_ERROR, 23 | CONFIG_CANT_OPEN_PROC_CMDLINE, 24 | CONFIG_SENSOR_ISNOT_SUPPORT, 25 | CONFIG_SENSOR_NOT_FOUND, 26 | }; 27 | 28 | enum ConfigError find_sections(struct IniConfig *ini); 29 | enum ConfigError section_pos( 30 | struct IniConfig *ini, const char *section, int *start_pos, int *end_pos); 31 | enum ConfigError parse_param_value( 32 | struct IniConfig *ini, const char *section, const char *param_name, 33 | char *param_value); 34 | enum ConfigError parse_enum( 35 | struct IniConfig *ini, const char *section, const char *param_name, 36 | void *enum_value, const char *possible_values[], 37 | const int possible_values_count, const int possible_values_offset); 38 | enum ConfigError parse_int( 39 | struct IniConfig *ini, const char *section, const char *param_name, 40 | const int min, const int max, int *int_value); 41 | enum ConfigError parse_bool( 42 | struct IniConfig *ini, const char *section, const char *param_name, 43 | bool *bool_value); 44 | enum ConfigError parse_array( 45 | struct IniConfig *ini, const char *section, const char *param_name, 46 | int *array, const int array_size); 47 | enum ConfigError parse_uint32( 48 | struct IniConfig *ini, const char *section, const char *param_name, 49 | const uint32_t min, const uint32_t max, uint32_t *value); 50 | -------------------------------------------------------------------------------- /src/config/sensor_config.c: -------------------------------------------------------------------------------- 1 | #include "sensor_config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../tools.h" 9 | 10 | struct SensorConfig sensor_config; 11 | 12 | enum ConfigError read_sensor_from_proc_cmdline(char *sensor_type) { 13 | FILE *file = fopen("/proc/cmdline", "r"); 14 | if (!file) 15 | return CONFIG_CANT_OPEN_PROC_CMDLINE; 16 | char cmdline[5 * 1024]; 17 | fread(cmdline, 1, 5 * 1024, file); 18 | fclose(file); 19 | 20 | regex_t regex; 21 | if (compile_regex( 22 | ®ex, "[[:space:]]*sensor=([[:alnum:]_]+)[[:space:]]*") < 0) { 23 | printf("compile_regex error\n"); 24 | return CONFIG_REGEX_ERROR; 25 | }; 26 | size_t n_matches = 2; // We have 1 capturing group + the whole match group 27 | regmatch_t m[n_matches]; 28 | int match = regexec(®ex, cmdline, n_matches, m, 0); 29 | if (match != 0) { 30 | regfree(®ex); 31 | printf("Can't parse sensor=xxxx from cmdline: \n'%s'\n", cmdline); 32 | return CONFIG_REGEX_ERROR; 33 | } 34 | sprintf( 35 | sensor_type, "%.*s", (int)(m[1].rm_eo - m[1].rm_so), 36 | cmdline + m[1].rm_so); 37 | return CONFIG_OK; 38 | } 39 | 40 | enum ConfigError parse_config_lvds( 41 | struct IniConfig *ini, const char *section, struct SensorLVDS *lvds) { 42 | enum ConfigError err; 43 | err = parse_int( 44 | ini, section, "img_size_w", INT_MIN, INT_MAX, &lvds->img_size_w); 45 | if (err != CONFIG_OK) 46 | return err; 47 | err = parse_int( 48 | ini, section, "img_size_h", INT_MIN, INT_MAX, &lvds->img_size_h); 49 | if (err != CONFIG_OK) 50 | return err; 51 | { 52 | const char *possible_values[] = { 53 | "HI_WDR_MODE_NONE", "HI_WDR_MODE_2F", "HI_WDR_MODE_3F", 54 | "HI_WDR_MODE_4F", "HI_WDR_MODE_DOL_2F", "HI_WDR_MODE_DOL_3F", 55 | "HI_WDR_MODE_DOL_4F"}; 56 | const int count = sizeof(possible_values) / sizeof(const char *); 57 | err = parse_enum( 58 | ini, section, "wdr_mode", (void *)&lvds->wdr_mode, possible_values, 59 | count, 0); 60 | if (err != CONFIG_OK) 61 | return err; 62 | } 63 | { 64 | const char *possible_values[] = { 65 | "LVDS_SYNC_MODE_SOL", "LVDS_SYNC_MODE_SAV"}; 66 | const int count = sizeof(possible_values) / sizeof(const char *); 67 | err = parse_enum( 68 | ini, section, "sync_mode", (void *)&lvds->sync_mode, 69 | possible_values, count, 0); 70 | if (err != CONFIG_OK) 71 | return err; 72 | } 73 | { 74 | const char *possible_values[] = { 75 | "RAW_DATA_8BIT", "RAW_DATA_10BIT", "RAW_DATA_12BIT", 76 | "RAW_DATA_14BIT"}; 77 | const int count = sizeof(possible_values) / sizeof(const char *); 78 | err = parse_enum( 79 | ini, section, "raw_data_type", (void *)&lvds->raw_data_type, 80 | possible_values, count, 1); 81 | if (err != CONFIG_OK) 82 | return err; 83 | } 84 | { 85 | const char *possible_values[] = { 86 | "LVDS_ENDIAN_LITTLE", "LVDS_ENDIAN_BIG"}; 87 | const int count = sizeof(possible_values) / sizeof(const char *); 88 | err = parse_enum( 89 | ini, section, "data_endian", (void *)&lvds->data_endian, 90 | possible_values, count, 1); 91 | if (err != CONFIG_OK) 92 | return err; 93 | err = parse_enum( 94 | ini, section, "sync_code_endian", (void *)&lvds->sync_code_endian, 95 | possible_values, count, 1); 96 | if (err != CONFIG_OK) 97 | return err; 98 | } 99 | err = parse_array(ini, section, "lane_id", lvds->lane_id, 8); 100 | if (err != CONFIG_OK) 101 | return err; 102 | err = parse_int( 103 | ini, section, "lvds_lane_num", INT_MIN, INT_MAX, &lvds->lvds_lane_num); 104 | if (err != CONFIG_OK) 105 | return err; 106 | err = parse_int( 107 | ini, section, "wdr_vc_num", INT_MIN, INT_MAX, &lvds->wdr_vc_num); 108 | if (err != CONFIG_OK) 109 | return err; 110 | err = parse_int( 111 | ini, section, "sync_code_num", INT_MIN, INT_MAX, &lvds->sync_code_num); 112 | if (err != CONFIG_OK) 113 | return err; 114 | char syncname[16]; 115 | for (int i = 0; i < 8; i++) { 116 | sprintf(syncname, "sync_code_%d", i); 117 | err = parse_array(ini, section, syncname, lvds->sync_code[i], 16); 118 | if (err != CONFIG_OK) 119 | return err; 120 | } 121 | return CONFIG_OK; 122 | } 123 | 124 | enum ConfigError parse_config_videv( 125 | struct IniConfig *ini, const char *section, struct SensorVIDEV *videv) { 126 | enum ConfigError err; 127 | { 128 | const char *possible_values[] = { 129 | "VI_INPUT_MODE_BT656", "VI_INPUT_MODE_BT601", 130 | "VI_INPUT_MODE_DIGITAL_CAMERA", "VI_INPUT_MODE_INTERLEAVED", 131 | "VI_INPUT_MODE_MIPI", "VI_INPUT_MODE_LVDS", 132 | "VI_INPUT_MODE_HISPI"}; 133 | const int count = sizeof(possible_values) / sizeof(const char *); 134 | err = parse_enum( 135 | ini, section, "Input_mod", (void *)&videv->input_mod, 136 | possible_values, count, 0); 137 | if (err != CONFIG_OK) 138 | return err; 139 | } 140 | { 141 | const char *possible_values[] = { 142 | "VI_WORK_MODE_1Multiplex", "VI_WORK_MODE_2Multiplex", 143 | "VI_WORK_MODE_4Multiplex"}; 144 | const int count = sizeof(possible_values) / sizeof(const char *); 145 | err = parse_enum( 146 | ini, section, "Work_mod", (void *)&videv->work_mod, possible_values, 147 | count, 0); 148 | if (err != CONFIG_OK) 149 | return err; 150 | } 151 | { 152 | const char *possible_values[] = { 153 | "VI_COMBINE_COMPOSITE", "VI_COMBINE_SEPARATE"}; 154 | const int count = sizeof(possible_values) / sizeof(const char *); 155 | err = parse_enum( 156 | ini, section, "Combine_mode", (void *)&videv->combine_mode, 157 | possible_values, count, 0); 158 | if (err != CONFIG_OK) 159 | return err; 160 | } 161 | { 162 | const char *possible_values[] = { 163 | "VI_COMP_MODE_SINGLE", "VI_COMP_MODE_DOUBLE"}; 164 | const int count = sizeof(possible_values) / sizeof(const char *); 165 | err = parse_enum( 166 | ini, section, "Comp_mode", (void *)&videv->comp_mode, 167 | possible_values, count, 0); 168 | if (err != CONFIG_OK) 169 | return err; 170 | } 171 | { 172 | const char *possible_values[] = { 173 | "VI_CLK_EDGE_SINGLE_UP", "VI_CLK_EDGE_SINGLE_DOWN", 174 | "VI_CLK_EDGE_DOUBLE"}; 175 | const int count = sizeof(possible_values) / sizeof(const char *); 176 | err = parse_enum( 177 | ini, section, "Clock_edge", (void *)&videv->clock_edge, 178 | possible_values, count, 0); 179 | if (err != CONFIG_OK) 180 | return err; 181 | } 182 | err = parse_uint32(ini, section, "Mask_num", 0, 2, &videv->mask_num); 183 | if (err != CONFIG_OK) 184 | return err; 185 | err = parse_uint32(ini, section, "Mask_0", 0, UINT_MAX, &videv->mask_0); 186 | if (err != CONFIG_OK) 187 | return err; 188 | err = parse_uint32(ini, section, "Mask_1", 0, UINT_MAX, &videv->mask_1); 189 | if (err != CONFIG_OK) 190 | return err; 191 | { 192 | const char *possible_values[] = { 193 | "VI_SCAN_INTERLACED", "VI_SCAN_PROGRESSIVE"}; 194 | const int count = sizeof(possible_values) / sizeof(const char *); 195 | err = parse_enum( 196 | ini, section, "Scan_mode", (void *)&videv->scan_mode, 197 | possible_values, count, 0); 198 | if (err != CONFIG_OK) 199 | return err; 200 | } 201 | { 202 | const char *possible_values[] = { 203 | "VI_INPUT_DATA_VUVU", "VI_INPUT_DATA_UVUV"}; 204 | const int count = sizeof(possible_values) / sizeof(const char *); 205 | err = parse_enum( 206 | ini, section, "Data_seq", (void *)&videv->data_seq, possible_values, 207 | count, 0); 208 | if (err != CONFIG_OK) { 209 | const char *possible_values[] = { 210 | "VI_INPUT_DATA_UYVY", "VI_INPUT_DATA_VYUY", 211 | "VI_INPUT_DATA_YUYV", "VI_INPUT_DATA_YVYU"}; 212 | const int count = sizeof(possible_values) / sizeof(const char *); 213 | err = parse_enum( 214 | ini, section, "Data_seq", (void *)&videv->data_seq, 215 | possible_values, count, 0); 216 | if (err != CONFIG_OK) 217 | return err; 218 | } 219 | } 220 | { 221 | const char *possible_values[] = {"VI_VSYNC_FIELD", "VI_VSYNC_PULSE"}; 222 | const int count = sizeof(possible_values) / sizeof(const char *); 223 | err = parse_enum( 224 | ini, section, "Vsync", (void *)&videv->vsync, possible_values, 225 | count, 0); 226 | if (err != CONFIG_OK) 227 | return err; 228 | } 229 | { 230 | const char *possible_values[] = { 231 | "VI_VSYNC_NEG_HIGH", "VI_VSYNC_NEG_LOW"}; 232 | const int count = sizeof(possible_values) / sizeof(const char *); 233 | err = parse_enum( 234 | ini, section, "VsyncNeg", (void *)&videv->vsync_neg, 235 | possible_values, count, 0); 236 | if (err != CONFIG_OK) 237 | return err; 238 | } 239 | { 240 | const char *possible_values[] = { 241 | "VI_HSYNC_VALID_SINGNAL", "VI_HSYNC_PULSE"}; 242 | const int count = sizeof(possible_values) / sizeof(const char *); 243 | err = parse_enum( 244 | ini, section, "Hsync", (void *)&videv->hsync, possible_values, 245 | count, 0); 246 | if (err != CONFIG_OK) 247 | return err; 248 | } 249 | { 250 | const char *possible_values[] = { 251 | "VI_HSYNC_NEG_HIGH", "VI_HSYNC_NEG_LOW"}; 252 | const int count = sizeof(possible_values) / sizeof(const char *); 253 | err = parse_enum( 254 | ini, section, "HsyncNeg", (void *)&videv->hsync_neg, 255 | possible_values, count, 0); 256 | if (err != CONFIG_OK) 257 | return err; 258 | } 259 | { 260 | const char *possible_values[] = { 261 | "VI_VSYNC_NORM_PULSE", "VI_VSYNC_VALID_SINGAL"}; 262 | const int count = sizeof(possible_values) / sizeof(const char *); 263 | err = parse_enum( 264 | ini, section, "VsyncValid", (void *)&videv->vsync_valid, 265 | possible_values, count, 0); 266 | if (err != CONFIG_OK) 267 | return err; 268 | } 269 | { 270 | const char *possible_values[] = { 271 | "VI_VSYNC_VALID_NEG_HIGH", "VI_VSYNC_VALID_NEG_LOW"}; 272 | const int count = sizeof(possible_values) / sizeof(const char *); 273 | err = parse_enum( 274 | ini, section, "VsyncValidNeg", (void *)&videv->vsync_valid_neg, 275 | possible_values, count, 0); 276 | if (err != CONFIG_OK) 277 | return err; 278 | } 279 | err = parse_int( 280 | ini, section, "Timingblank_HsyncHfb", 0, INT_MAX, 281 | &videv->timing_blank_hsync_hfb); 282 | if (err != CONFIG_OK) 283 | return err; 284 | err = parse_int( 285 | ini, section, "Timingblank_HsyncAct", 0, INT_MAX, 286 | &videv->timing_blank_hsync_act); 287 | if (err != CONFIG_OK) 288 | return err; 289 | err = parse_int( 290 | ini, section, "Timingblank_HsyncHbb", 0, INT_MAX, 291 | &videv->timing_blank_hsync_hbb); 292 | if (err != CONFIG_OK) 293 | return err; 294 | err = parse_int( 295 | ini, section, "Timingblank_VsyncVfb", 0, INT_MAX, 296 | &videv->timing_blank_vsync_vfb); 297 | if (err != CONFIG_OK) 298 | return err; 299 | err = parse_int( 300 | ini, section, "Timingblank_VsyncVact", 0, INT_MAX, 301 | &videv->timing_blank_vsync_vact); 302 | if (err != CONFIG_OK) 303 | return err; 304 | err = parse_int( 305 | ini, section, "Timingblank_VsyncVbb", 0, INT_MAX, 306 | &videv->timing_blank_vsync_vbb); 307 | if (err != CONFIG_OK) 308 | return err; 309 | err = parse_int( 310 | ini, section, "Timingblank_VsyncVbfb", 0, INT_MAX, 311 | &videv->timing_blank_vsync_vbfb); 312 | if (err != CONFIG_OK) 313 | return err; 314 | err = parse_int( 315 | ini, section, "Timingblank_VsyncVbact", 0, INT_MAX, 316 | &videv->timing_blank_vsync_vbact); 317 | if (err != CONFIG_OK) 318 | return err; 319 | err = parse_int( 320 | ini, section, "Timingblank_VsyncVbbb", 0, INT_MAX, 321 | &videv->timing_blank_vsync_vbbb); 322 | if (err != CONFIG_OK) 323 | return err; 324 | { 325 | const char *possible_values[] = {"BT656_FIXCODE_1", "BT656_FIXCODE_0"}; 326 | const int count = sizeof(possible_values) / sizeof(const char *); 327 | err = parse_enum( 328 | ini, section, "FixCode", (void *)&videv->fix_code, possible_values, 329 | count, 0); 330 | if (err != CONFIG_OK) 331 | return err; 332 | } 333 | { 334 | const char *possible_values[] = { 335 | "BT656_FIELD_POLAR_STD", "BT656_FIELD_POLAR_NSTD"}; 336 | const int count = sizeof(possible_values) / sizeof(const char *); 337 | err = parse_enum( 338 | ini, section, "FieldPolar", (void *)&videv->field_polar, 339 | possible_values, count, 0); 340 | if (err != CONFIG_OK) 341 | return err; 342 | } 343 | { 344 | const char *possible_values[] = { 345 | "VI_PATH_BYPASS", "VI_PATH_ISP", "VI_PATH_RAW"}; 346 | const int count = sizeof(possible_values) / sizeof(const char *); 347 | err = parse_enum( 348 | ini, section, "DataPath", (void *)&videv->data_path, 349 | possible_values, count, 0); 350 | if (err != CONFIG_OK) 351 | return err; 352 | } 353 | { 354 | const char *possible_values[] = { 355 | "VI_DATA_TYPE_YUV", "VI_DATA_TYPE_RGB"}; 356 | const int count = sizeof(possible_values) / sizeof(const char *); 357 | err = parse_enum( 358 | ini, section, "InputDataType", &videv->input_data_type, 359 | possible_values, count, 0); 360 | if (err != CONFIG_OK) 361 | return err; 362 | } 363 | err = parse_int(ini, section, "DataRev", 0, INT_MAX, &videv->data_rev); 364 | if (err != CONFIG_OK) 365 | return err; 366 | err = parse_int(ini, section, "DevRect_x", 0, INT_MAX, &videv->dev_rect_x); 367 | if (err != CONFIG_OK) 368 | return err; 369 | err = parse_int(ini, section, "DevRect_y", 0, INT_MAX, &videv->dev_rect_y); 370 | if (err != CONFIG_OK) 371 | return err; 372 | err = parse_int(ini, section, "DevRect_w", 0, INT_MAX, &videv->dev_rect_w); 373 | if (err != CONFIG_OK) 374 | return err; 375 | err = parse_int(ini, section, "DevRect_h", 0, INT_MAX, &videv->dev_rect_h); 376 | if (err != CONFIG_OK) 377 | return err; 378 | return CONFIG_OK; 379 | } 380 | 381 | int parse_config_vichn( 382 | struct IniConfig *ini, const char *section, struct SensorVIChn *vichn) { 383 | enum ConfigError err; 384 | err = parse_int(ini, section, "CapRect_X", 0, INT_MAX, &vichn->cap_rect_x); 385 | if (err != CONFIG_OK) 386 | return err; 387 | err = parse_int(ini, section, "CapRect_Y", 0, INT_MAX, &vichn->cap_rect_y); 388 | if (err != CONFIG_OK) 389 | return err; 390 | err = parse_int( 391 | ini, section, "CapRect_Width", 0, INT_MAX, &vichn->cap_rect_width); 392 | if (err != CONFIG_OK) 393 | return err; 394 | err = parse_int( 395 | ini, section, "CapRect_Height", 0, INT_MAX, &vichn->cap_rect_height); 396 | if (err != CONFIG_OK) 397 | return err; 398 | err = parse_int( 399 | ini, section, "DestSize_Width", 0, INT_MAX, &vichn->dest_size_width); 400 | if (err != CONFIG_OK) 401 | return err; 402 | err = parse_int( 403 | ini, section, "DestSize_Height", 0, INT_MAX, &vichn->dest_size_height); 404 | if (err != CONFIG_OK) 405 | return err; 406 | { 407 | const char *possible_values[] = { 408 | "VI_CAPSEL_TOP", "VI_CAPSEL_BOTTOM", "VI_CAPSEL_BOTH"}; 409 | const int count = sizeof(possible_values) / sizeof(const char *); 410 | err = parse_enum( 411 | ini, section, "CapSel", (void *)&vichn->cap_sel, possible_values, 412 | count, 0); 413 | if (err != CONFIG_OK) 414 | return err; 415 | } 416 | { 417 | const char *possible_values[] = { 418 | "PIXEL_FORMAT_RGB_1BPP", 419 | "PIXEL_FORMAT_RGB_2BPP", 420 | "PIXEL_FORMAT_RGB_4BPP", 421 | "PIXEL_FORMAT_RGB_8BPP", 422 | "PIXEL_FORMAT_RGB_444", 423 | "PIXEL_FORMAT_RGB_4444", 424 | "PIXEL_FORMAT_RGB_555", 425 | "PIXEL_FORMAT_RGB_565", 426 | "PIXEL_FORMAT_RGB_1555", 427 | "PIXEL_FORMAT_RGB_888", 428 | "PIXEL_FORMAT_RGB_8888", 429 | "PIXEL_FORMAT_RGB_PLANAR_888", 430 | "PIXEL_FORMAT_RGB_BAYER_8BPP", 431 | "PIXEL_FORMAT_RGB_BAYER_10BPP", 432 | "PIXEL_FORMAT_RGB_BAYER_12BPP", 433 | "PIXEL_FORMAT_RGB_BAYER_14BPP", 434 | "PIXEL_FORMAT_RGB_BAYER", 435 | "PIXEL_FORMAT_YUV_A422", 436 | "PIXEL_FORMAT_YUV_A444", 437 | "PIXEL_FORMAT_YUV_PLANAR_422", 438 | "PIXEL_FORMAT_YUV_PLANAR_420", 439 | "PIXEL_FORMAT_YUV_PLANAR_444", 440 | "PIXEL_FORMAT_YUV_SEMIPLANAR_422", 441 | "PIXEL_FORMAT_YUV_SEMIPLANAR_420", 442 | "PIXEL_FORMAT_YUV_SEMIPLANAR_444", 443 | "PIXEL_FORMAT_UYVY_PACKAGE_422", 444 | "PIXEL_FORMAT_YUYV_PACKAGE_422", 445 | "PIXEL_FORMAT_VYUY_PACKAGE_422", 446 | "PIXEL_FORMAT_YCbCr_PLANAR", 447 | "PIXEL_FORMAT_YUV_400"}; 448 | const int count = sizeof(possible_values) / sizeof(const char *); 449 | err = parse_enum( 450 | ini, section, "PixFormat", (void *)&vichn->pix_format, 451 | possible_values, count, 0); 452 | if (err != CONFIG_OK) 453 | return err; 454 | } 455 | { 456 | const char *possible_values[] = { 457 | "COMPRESS_MODE_NONE", "COMPRESS_MODE_SEG", "COMPRESS_MODE_SEG128", 458 | "COMPRESS_MODE_LINE", "COMPRESS_MODE_FRAME"}; 459 | const int count = sizeof(possible_values) / sizeof(const char *); 460 | err = parse_enum( 461 | ini, section, "CompressMode", (void *)&vichn->compress_mode, 462 | possible_values, count, 0); 463 | if (err != CONFIG_OK) 464 | return err; 465 | } 466 | err = parse_int( 467 | ini, section, "SrcFrameRate", INT_MIN, INT_MAX, &vichn->src_frame_rate); 468 | if (err != CONFIG_OK) 469 | return err; 470 | err = parse_int( 471 | ini, section, "FrameRate", INT_MIN, INT_MAX, &vichn->frame_rate); 472 | if (err != CONFIG_OK) 473 | return err; 474 | return CONFIG_OK; 475 | } 476 | 477 | enum ConfigError parse_config_isp( 478 | struct IniConfig *ini, const char *section, struct SensorISP *isp) { 479 | enum ConfigError err; 480 | err = parse_int(ini, "isp_image", "Isp_x", 0, INT_MAX, &isp->isp_x); 481 | if (err != CONFIG_OK) 482 | return err; 483 | err = parse_int(ini, "isp_image", "Isp_y", 0, INT_MAX, &isp->isp_y); 484 | if (err != CONFIG_OK) 485 | return err; 486 | err = parse_int(ini, "isp_image", "Isp_W", 0, INT_MAX, &isp->isp_w); 487 | if (err != CONFIG_OK) 488 | return err; 489 | err = parse_int(ini, "isp_image", "Isp_H", 0, INT_MAX, &isp->isp_h); 490 | if (err != CONFIG_OK) 491 | return err; 492 | err = parse_int( 493 | ini, "isp_image", "Isp_FrameRate", 0, INT_MAX, &isp->isp_frame_rate); 494 | if (err != CONFIG_OK) 495 | return err; 496 | { 497 | const char *possible_values[] = { 498 | "BAYER_RGGB", "BAYER_GRBG", "BAYER_GBRG", "BAYER_BGGR"}; 499 | const int count = sizeof(possible_values) / sizeof(const char *); 500 | err = parse_enum( 501 | ini, "isp_image", "isp_bayer", (void *)&isp->isp_bayer, 502 | possible_values, count, 0); 503 | if (err != CONFIG_OK) 504 | return err; 505 | } 506 | return CONFIG_OK; 507 | } 508 | 509 | enum ConfigError parse_sensor_config(char *path, struct SensorConfig *config) { 510 | if (config == NULL) 511 | return -1; 512 | memset(config, 0, sizeof(struct SensorConfig)); 513 | struct IniConfig ini; 514 | memset(&ini, 0, sizeof(struct IniConfig)); 515 | enum ConfigError err; 516 | 517 | // load config file to string 518 | ini.str = NULL; 519 | { 520 | FILE *file = fopen(path, "rb"); 521 | if (!file) { 522 | printf("Can't open file %s\n", path); 523 | return -1; 524 | } 525 | 526 | fseek(file, 0, SEEK_END); 527 | size_t length = (size_t)ftell(file); 528 | fseek(file, 0, SEEK_SET); 529 | 530 | ini.str = malloc(length + 1); 531 | if (!ini.str) { 532 | printf("Can't allocate buf in parse_sensor_config\n"); 533 | fclose(file); 534 | return -1; 535 | } 536 | size_t n = fread(ini.str, 1, length, file); 537 | if (n != length) { 538 | printf("Can't read all file %s\n", path); 539 | fclose(file); 540 | free(ini.str); 541 | return -1; 542 | } 543 | fclose(file); 544 | ini.str[length] = 0; 545 | } 546 | 547 | find_sections(&ini); 548 | 549 | // [sensor] 550 | err = parse_param_value(&ini, "sensor", "sensor_type", config->sensor_type); 551 | if (err != CONFIG_OK) 552 | goto RET_ERR; 553 | { 554 | const char *possible_values[] = { 555 | "WDR_MODE_NONE", 556 | "WDR_MODE_BUILT_IN", 557 | "WDR_MODE_2To1_LINE", 558 | "WDR_MODE_2To1_FRAME", 559 | "WDR_MODE_2To1_FRAME_FULL_RATE", 560 | "WDR_MODE_3To1_LINE", 561 | "WDR_MODE_3To1_FRAME", 562 | "WDR_MODE_3To1_FRAME_FULL_RATE", 563 | "WDR_MODE_4To1_LINE", 564 | "WDR_MODE_4To1_FRAME", 565 | "WDR_MODE_4To1_FRAME_FULL_RATE"}; 566 | const int count = sizeof(possible_values) / sizeof(const char *); 567 | err = parse_enum( 568 | &ini, "sensor", "mode", (void *)&config->mode, possible_values, 569 | count, 0); 570 | if (err != CONFIG_OK) 571 | goto RET_ERR; 572 | } 573 | err = parse_param_value(&ini, "sensor", "DllFile", config->dll_file); 574 | if (err != CONFIG_OK) 575 | goto RET_ERR; 576 | 577 | // [mode] 578 | { 579 | const char *possible_values[] = { 580 | "INPUT_MODE_MIPI", "INPUT_MODE_SUBLVDS", "INPUT_MODE_LVDS", 581 | "INPUT_MODE_HISPI", "INPUT_MODE_CMOS_18V", "INPUT_MODE_CMOS_33V", 582 | "INPUT_MODE_BT1120", "INPUT_MODE_BYPASS"}; 583 | const int count = sizeof(possible_values) / sizeof(const char *); 584 | err = parse_enum( 585 | &ini, "mode", "input_mode", (void *)&config->input_mode, 586 | possible_values, count, 0); 587 | if (err != CONFIG_OK) 588 | goto RET_ERR; 589 | } 590 | 591 | if (config->input_mode == INPUT_MODE_MIPI) { 592 | // [mipi] 593 | { 594 | const char *possible_values[] = {// starts from 1 !!!!! 595 | "RAW_DATA_8BIT", "RAW_DATA_10BIT", 596 | "RAW_DATA_12BIT", 597 | "RAW_DATA_14BIT"}; 598 | const int count = sizeof(possible_values) / sizeof(const char *); 599 | err = parse_enum( 600 | &ini, "mipi", "data_type", (void *)&config->mipi.data_type, 601 | possible_values, count, 1); 602 | if (err != CONFIG_OK) 603 | goto RET_ERR; 604 | } 605 | err = parse_array(&ini, "mipi", "lane_id", config->mipi.lane_id, 8); 606 | if (err != CONFIG_OK) 607 | goto RET_ERR; 608 | } else if (config->input_mode == INPUT_MODE_LVDS) { 609 | // [lvds] 610 | err = parse_config_lvds(&ini, "lvds", &config->lvds); 611 | if (err != CONFIG_OK) 612 | goto RET_ERR; 613 | } 614 | 615 | // [isp_image] 616 | err = parse_config_isp(&ini, "ips_image", &config->isp); 617 | if (err != CONFIG_OK) 618 | goto RET_ERR; 619 | // [vi_dev] 620 | err = parse_config_videv(&ini, "vi_dev", &config->videv); 621 | if (err != CONFIG_OK) 622 | goto RET_ERR; 623 | // [vi_chn] 624 | err = parse_config_vichn(&ini, "vi_chn", &config->vichn); 625 | if (err != CONFIG_OK) 626 | goto RET_ERR; 627 | 628 | sensor_config = *config; 629 | free(ini.str); 630 | return CONFIG_OK; 631 | RET_ERR: 632 | free(ini.str); 633 | return err; 634 | } 635 | -------------------------------------------------------------------------------- /src/config/sensor_config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "config.h" 4 | #include "compat.h" 5 | 6 | struct SensorMIPI { 7 | raw_data_type_e data_type; 8 | int lane_id[8]; 9 | }; 10 | 11 | struct SensorLVDS { 12 | int img_size_w; 13 | int img_size_h; 14 | wdr_mode_t wdr_mode; 15 | lvds_sync_mode_t sync_mode; 16 | data_type_t raw_data_type; 17 | lvds_bit_endian_t data_endian; 18 | lvds_bit_endian_t sync_code_endian; 19 | int lane_id[8]; 20 | int lvds_lane_num; 21 | int wdr_vc_num; 22 | int sync_code_num; 23 | int sync_code[8][16]; 24 | }; 25 | 26 | struct SensorVIDEV { 27 | VI_INPUT_MODE_E input_mod; 28 | VI_WORK_MODE_E work_mod; 29 | VI_COMBINE_MODE_E combine_mode; 30 | VI_COMP_MODE_E comp_mode; 31 | VI_CLK_EDGE_E clock_edge; 32 | unsigned int mask_num; 33 | unsigned int mask_0; 34 | unsigned int mask_1; 35 | VI_SCAN_MODE_E scan_mode; 36 | VI_DATA_YUV_SEQ_E data_seq; 37 | VI_VSYNC_E vsync; 38 | VI_VSYNC_NEG_E vsync_neg; 39 | VI_HSYNC_E hsync; 40 | VI_HSYNC_NEG_E hsync_neg; 41 | VI_VSYNC_VALID_E vsync_valid; 42 | VI_VSYNC_VALID_NEG_E vsync_valid_neg; 43 | unsigned int timing_blank_hsync_hfb; 44 | unsigned int timing_blank_hsync_act; 45 | unsigned int timing_blank_hsync_hbb; 46 | unsigned int timing_blank_vsync_vfb; 47 | unsigned int timing_blank_vsync_vact; 48 | unsigned int timing_blank_vsync_vbb; 49 | unsigned int timing_blank_vsync_vbfb; 50 | unsigned int timing_blank_vsync_vbact; 51 | unsigned int timing_blank_vsync_vbbb; 52 | 53 | BT656_FIXCODE_E fix_code; 54 | BT656_FIELD_POLAR_E field_polar; 55 | VI_DATA_PATH_E data_path; 56 | VI_DATA_TYPE_E input_data_type; 57 | int data_rev; 58 | int dev_rect_x; 59 | int dev_rect_y; 60 | unsigned int dev_rect_w; 61 | unsigned int dev_rect_h; 62 | }; 63 | 64 | struct SensorISP { 65 | int isp_x; 66 | int isp_y; 67 | unsigned int isp_h; 68 | unsigned int isp_w; 69 | unsigned int isp_frame_rate; 70 | ISP_BAYER_FORMAT_E isp_bayer; 71 | }; 72 | 73 | struct SensorVIChn { 74 | int cap_rect_x; 75 | int cap_rect_y; 76 | unsigned int cap_rect_width; 77 | unsigned int cap_rect_height; 78 | unsigned int dest_size_width; 79 | unsigned int dest_size_height; 80 | VI_CAPSEL_E cap_sel; 81 | PIXEL_FORMAT_E pix_format; 82 | COMPRESS_MODE_E compress_mode; 83 | int src_frame_rate; 84 | int frame_rate; 85 | }; 86 | 87 | struct SensorConfig { 88 | // [sensor] 89 | char sensor_type[128]; 90 | WDR_MODE_E mode; 91 | char dll_file[256]; 92 | 93 | // [mode] 94 | input_mode_t input_mode; 95 | 96 | // [mipi] 97 | struct SensorMIPI mipi; 98 | 99 | // [lvds] 100 | struct SensorLVDS lvds; 101 | 102 | // [isp_image] 103 | struct SensorISP isp; 104 | 105 | // [vi_dev] 106 | struct SensorVIDEV videv; 107 | 108 | // [vi_chn] 109 | struct SensorVIChn vichn; 110 | }; 111 | 112 | extern struct SensorConfig sensor_config; 113 | 114 | enum ConfigError parse_sensor_config(char *path, struct SensorConfig *config); 115 | -------------------------------------------------------------------------------- /src/gpio.c: -------------------------------------------------------------------------------- 1 | #include "gpio.h" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | enum PIN_DIRECTION { INPUT, OUTPUT }; 16 | 17 | struct Pin *find_pin(const uint8_t port, const uint8_t pin) { 18 | for (int i = 0; i < get_pins_hi3518EV200_size(); ++i) { 19 | struct Pin *p = &get_pins_hi3518EV200()[i]; 20 | if (p->port == port && p->pin == pin) { 21 | return p; 22 | } 23 | } 24 | return 0; 25 | } 26 | 27 | bool pin_linux_to_port_pin( 28 | const uint8_t pin_number, uint8_t *port, uint8_t *pin) { 29 | *port = pin_number / 8; 30 | *pin = pin_number - (*port) * 8; 31 | return true; 32 | } 33 | 34 | bool set_u8(const uint32_t offset, const uint8_t value) { 35 | int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); 36 | if (mem_fd < 0) { 37 | printf("set_u8 can't open /dev/mem \n"); 38 | return false; 39 | } 40 | volatile char *mmap_ptr = mmap( 41 | (void *)offset, // Any adddress in our space will do 42 | 1, // Map length 43 | PROT_WRITE, // Enable reading & writting to mapped memory 44 | MAP_SHARED, // Shared with other processes 45 | mem_fd, // File to map 46 | 0 // Offset to base address 47 | ); 48 | if (mmap_ptr == MAP_FAILED) { 49 | printf("set_u8 mmap_ptr mmap error %d\n", (int)mmap_ptr); 50 | printf("set_u8 Error: %s (%d)\n", strerror(errno), errno); 51 | close(mem_fd); 52 | return false; 53 | } 54 | mmap_ptr[0] = value; 55 | close(mem_fd); 56 | return true; 57 | } 58 | 59 | bool get_u8(const uint32_t offset, enum PIN_DIRECTION *value) { 60 | int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); 61 | if (mem_fd < 0) { 62 | printf("get_u8 can't open /dev/mem \n"); 63 | return false; 64 | } 65 | volatile char *mmap_ptr = mmap( 66 | (void *)offset, // Any adddress in our space will do 67 | 1, // Map length 68 | PROT_READ, // Enable reading & writting to mapped memory 69 | MAP_SHARED, // Shared with other processes 70 | mem_fd, // File to map 71 | 0 // Offset to base address 72 | ); 73 | if (mmap_ptr == MAP_FAILED) { 74 | printf("get_u8 mmap_ptr mmap error %d\n", (int)mmap_ptr); 75 | printf("get_u8 Error: %s (%d)\n", strerror(errno), errno); 76 | close(mem_fd); 77 | return false; 78 | } 79 | *value = mmap_ptr[0]; 80 | close(mem_fd); 81 | return true; 82 | } 83 | 84 | bool set_u32(const uint32_t offset, const uint32_t value) { 85 | int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); 86 | if (mem_fd < 0) { 87 | printf("set_u32 can't open /dev/mem \n"); 88 | return false; 89 | } 90 | volatile char *mmap_ptr = mmap( 91 | (void *)offset, // Any adddress in our space will do 92 | 4, // Map length 93 | PROT_WRITE, // Enable reading & writting to mapped memory 94 | MAP_SHARED, // Shared with other processes 95 | mem_fd, // File to map 96 | 0 // Offset to base address 97 | ); 98 | if (mmap_ptr == MAP_FAILED) { 99 | printf("set_u32 mmap_ptr mmap error %d\n", (int)mmap_ptr); 100 | printf("set_u32 Error: %s (%d)\n", strerror(errno), errno); 101 | close(mem_fd); 102 | return false; 103 | } 104 | mmap_ptr[0] = ((uint8_t *)&value)[0]; 105 | mmap_ptr[1] = ((uint8_t *)&value)[1]; 106 | mmap_ptr[2] = ((uint8_t *)&value)[2]; 107 | mmap_ptr[3] = ((uint8_t *)&value)[3]; 108 | close(mem_fd); 109 | return true; 110 | } 111 | 112 | bool get_u32(const uint32_t offset, uint32_t *value) { 113 | int mem_fd = open("/dev/mem", O_RDWR | O_SYNC); 114 | if (mem_fd < 0) { 115 | printf("get_u32 can't open /dev/mem \n"); 116 | return false; 117 | } 118 | volatile char *mmap_ptr = mmap( 119 | (void *)offset, // Any adddress in our space will do 120 | 4, // Map length 121 | PROT_READ, // Enable reading & writting to mapped memory 122 | MAP_SHARED, // Shared with other processes 123 | mem_fd, // File to map 124 | 0 // Offset to base address 125 | ); 126 | if (mmap_ptr == MAP_FAILED) { 127 | printf("get_u32 mmap_ptr mmap error %d\n", (int)mmap_ptr); 128 | printf("get_u32 Error: %s (%d)\n", strerror(errno), errno); 129 | close(mem_fd); 130 | return false; 131 | } 132 | uint32_t val = 0; 133 | val += mmap_ptr[0]; 134 | val = val << 8; 135 | value += mmap_ptr[1]; 136 | val = val << 8; 137 | val += mmap_ptr[2]; 138 | val = val << 8; 139 | val += mmap_ptr[3]; 140 | *value = val; 141 | close(mem_fd); 142 | return true; 143 | } 144 | 145 | bool get_bit(const uint8_t value, const uint8_t bit_pos) { 146 | const uint8_t bit = value >> bit_pos & 1; 147 | return bit == 1; 148 | } 149 | 150 | uint8_t set_bit(const uint8_t value, const uint8_t bit_pos, const bool bit) { 151 | if (bit_pos >= 8) { 152 | return value; 153 | } 154 | if (bit) { 155 | return value | (1 << bit_pos); 156 | } // set pin bit to 1 157 | else { 158 | return value & !(1 << bit_pos); 159 | } // set pin bit to 0 160 | } 161 | 162 | bool set_pin_dir(struct Pin *pin, const enum PIN_DIRECTION direction) { 163 | if (!set_u32(IOMUX + pin->muxctl, pin->e)) 164 | return false; 165 | 166 | const uint32_t gpio_port_addr = GPIO[pin->port]; 167 | const uint32_t gpio_dir_addr = gpio_port_addr + GPIO_DIR; 168 | enum PIN_DIRECTION gpio_port_direction; 169 | if (!get_u8(gpio_dir_addr, &gpio_port_direction)) 170 | return false; 171 | switch (direction) { 172 | case OUTPUT: { 173 | gpio_port_direction = set_bit(gpio_port_direction, pin->pin, true); 174 | break; 175 | } // set pin bit to 1 for output 176 | case INPUT: { 177 | gpio_port_direction = set_bit(gpio_port_direction, pin->pin, false); 178 | break; 179 | } // set pin bit to 0 for output 180 | } 181 | if (!set_u8(gpio_dir_addr, gpio_port_direction)) 182 | return false; 183 | return true; 184 | } 185 | 186 | bool get_pin(const uint8_t port, const uint8_t pin, bool *value) { 187 | struct Pin *p = find_pin(port, pin); 188 | if (!p) { 189 | return false; 190 | } 191 | 192 | if (!set_pin_dir(p, INPUT)) 193 | return false; 194 | 195 | uint32_t gpio_port_addr = GPIO[port]; 196 | gpio_port_addr = gpio_port_addr + 0x03FC; 197 | enum PIN_DIRECTION val; 198 | if (!get_u8(gpio_port_addr, &val)) 199 | return false; 200 | uint8_t bit = (val >> pin & 1); 201 | *value = (bit == 1); 202 | return true; 203 | } 204 | 205 | bool get_pin_linux(const uint8_t pin_number, bool *value) { 206 | uint8_t port, pin; 207 | if (!pin_linux_to_port_pin(pin_number, &port, &pin)) 208 | return false; 209 | if (!get_pin(port, pin, value)) 210 | return false; 211 | return true; 212 | } 213 | 214 | bool set_pin(struct Pin *pin, const bool bit) { 215 | if (!pin) 216 | return false; 217 | uint32_t gpio_port_addr = GPIO[pin->port]; 218 | gpio_port_addr = gpio_port_addr + (1 << (((uint32_t)pin->pin) + 2)); 219 | if (bit) { 220 | if (!set_u8(gpio_port_addr, 0xff)) 221 | return false; 222 | } else { 223 | if (!set_u8(gpio_port_addr, 0x00)) 224 | return false; 225 | } 226 | return true; 227 | } 228 | 229 | bool set_port_pin(const uint8_t port, const uint8_t pin, const bool bit) { 230 | struct Pin *p = find_pin(port, pin); 231 | if (!pin) 232 | return false; 233 | if (!set_pin_dir(p, OUTPUT)) 234 | return false; 235 | if (!set_pin(p, bit)) 236 | return false; 237 | return true; 238 | } 239 | bool set_pin_linux(const uint8_t pin_number, const bool bit) { 240 | uint8_t port, pin; 241 | if (!pin_linux_to_port_pin(pin_number, &port, &pin)) 242 | return false; 243 | if (!set_port_pin(port, pin, bit)) 244 | return false; 245 | return true; 246 | } 247 | 248 | // bit mask 249 | // 0x0004 = 0000000100 250 | // 0x0008 = 0000001000 251 | // 0x0010 = 0000010000 252 | // 0x0020 = 0000100000 253 | // 0x0040 = 0001000000 254 | // 0x0080 = 0010000000 255 | // 0x0100 = 0100000000 256 | // 0x0200 = 1000000000 257 | // 0x03FC = 1111111100 258 | 259 | static struct Pin hi3518EV200_pins[] = { 260 | {.gpio = true, 261 | .port = 0, 262 | .pin = 6, 263 | .muxctl = 0x008, 264 | .e = 0b0, 265 | .desc = "muxctrl_reg02=008 000: GPIO0_6, 001: FLASH_TRIG, 010: " 266 | "SFC_EMMC_BOOT_MODE, 011: SPI1_CSN1, 100: VI_VS_BT1120"}, 267 | {.gpio = true, 268 | .port = 0, 269 | .pin = 5, 270 | .muxctl = 0x004, 271 | .e = 0b1, 272 | .desc = "muxctrl_reg01=004 0: SENSOR_RSTN, 1: GPIO0_5"}, 273 | {.gpio = true, 274 | .port = 0, 275 | .pin = 7, 276 | .muxctl = 0x00C, 277 | .e = 0b0, 278 | .desc = "muxctrl_reg03=00C 000: GPIO0_7, 001: SHUTTER_TRIG, 010: " 279 | "SFC_DEVICE_MODE, 100: VI_HS_BT1120"}, 280 | {.gpio = false, 281 | .port = 2, 282 | .pin = 0, 283 | .muxctl = 0x010, 284 | .e = 0b0, 285 | .desc = "muxctrl_reg04=010 000: GPIO2_0, 001: RMII_CLK, 011: VO_CLK, 100: " 286 | "SDIO1_CCLK_OUT"}, 287 | {.gpio = false, 288 | .port = 2, 289 | .pin = 1, 290 | .muxctl = 0x014, 291 | .e = 0b0, 292 | .desc = "muxctrl_reg05=014 000: GPIO2_1, 001: RMII_TX_EN, 011: VO_VS, " 293 | "100: SDIO1_CARD_DETECT"}, 294 | {.gpio = false, 295 | .port = 2, 296 | .pin = 2, 297 | .muxctl = 0x018, 298 | .e = 0b0, 299 | .desc = "muxctrl_reg06=018 000: GPIO2_2, 001: RMII_TXD0, 011: VO_DATA5, " 300 | "100: SDIO1_CWPR"}, 301 | {.gpio = false, 302 | .port = 2, 303 | .pin = 3, 304 | .muxctl = 0x01C, 305 | .e = 0b0, 306 | .desc = "muxctrl_reg07=01C 000: GPIO2_3, 001: RMII_TXD1, 011: VO_DE, 100: " 307 | "SDIO1_CDATA1"}, 308 | {.gpio = false, 309 | .port = 2, 310 | .pin = 4, 311 | .muxctl = 0x020, 312 | .e = 0b0, 313 | .desc = "muxctrl_reg08=020 000: GPIO2_4, 001: RMII_RX_DV, 011: VO_DATA7, " 314 | "100: SDIO1_CDATA0"}, 315 | {.gpio = false, 316 | .port = 2, 317 | .pin = 5, 318 | .muxctl = 0x024, 319 | .e = 0b0, 320 | .desc = "muxctrl_reg09=024 000: GPIO2_5, 001: RMII_RXD0, 011: VO_DATA2, " 321 | "100: SDIO1_CDATA3"}, 322 | {.gpio = false, 323 | .port = 2, 324 | .pin = 6, 325 | .muxctl = 0x028, 326 | .e = 0b0, 327 | .desc = "muxctrl_reg10=028 000: GPIO2_6, 001: RMII_RXD1, 011: VO_DATA3, " 328 | "100: SDIO1_CCMD"}, 329 | {.gpio = false, 330 | .port = 2, 331 | .pin = 7, 332 | .muxctl = 0x02C, 333 | .e = 0b0, 334 | .desc = "muxctrl_reg11=02C 000: GPIO2_7, 001: EPHY_RST, 010: BOOT_SEL, " 335 | "011: VO_HS, 100: SDIO1_CARD_POWER_EN"}, 336 | {.gpio = true, 337 | .port = 0, 338 | .pin = 3, 339 | .muxctl = 0x030, 340 | .e = 0b0, 341 | .desc = "muxctrl_reg12=030 00: GPIO0_3, 01: SPI1_CSN1, 11: VO_DATA0"}, 342 | {.gpio = false, 343 | .port = 3, 344 | .pin = 0, 345 | .muxctl = 0x034, 346 | .e = 0b0, 347 | .desc = "muxctrl_reg13=034 000: GPIO3_0, 001: EPHY_CLK, 011: VO_DATA1, " 348 | "100: SDIO1_CDATA2"}, 349 | // // Pin { gpio: false, port: 3, pin: 1, muxctl: 0x038, e: 0b0, 350 | // desc: "muxctrl_reg14=038 00: GPIO3_1, 01: MDCK, 10: BOOTROM_SEL, 351 | // 11: VO_DATA6"}, 352 | // // Pin { gpio: false, port: 3, pin: 2, muxctl: 0x03C, e: 0b0, 353 | // desc: "muxctrl_reg15=03C 00: GPIO3_2, 01: MDIO, 11: VO_DATA4"}, 354 | {.gpio = true, 355 | .port = 3, 356 | .pin = 3, 357 | .muxctl = 0x040, 358 | .e = 0b0, 359 | .desc = "muxctrl_reg16=040 00: GPIO3_3, 01: SPI0_SCLK, 10: I2C0_SCL"}, 360 | {.gpio = true, 361 | .port = 3, 362 | .pin = 4, 363 | .muxctl = 0x044, 364 | .e = 0b0, 365 | .desc = "muxctrl_reg17=044 00: GPIO3_4, 01: SPI0_SDO, 10: I2C0_SDA"}, 366 | {.gpio = true, 367 | .port = 3, 368 | .pin = 5, 369 | .muxctl = 0x048, 370 | .e = 0b0, 371 | .desc = "muxctrl_reg18=048 0: GPIO3_5, 1: SPI0_SDI"}, 372 | {.gpio = true, 373 | .port = 3, 374 | .pin = 6, 375 | .muxctl = 0x04C, 376 | .e = 0b0, 377 | .desc = "muxctrl_reg19=04C 0: GPIO3_6, 1: SPI0_CSN"}, 378 | {.gpio = true, 379 | .port = 3, 380 | .pin = 7, 381 | .muxctl = 0x050, 382 | .e = 0b0, 383 | .desc = "muxctrl_reg20=050 00: GPIO3_7, 01: SPI1_SCLK, 10: I2C1_SCL"}, 384 | {.gpio = true, 385 | .port = 4, 386 | .pin = 0, 387 | .muxctl = 0x054, 388 | .e = 0b0, 389 | .desc = "muxctrl_reg21=054 00: GPIO4_0, 01: SPI1_SDO, 10: I2C1_SDA"}, 390 | {.gpio = true, 391 | .port = 4, 392 | .pin = 1, 393 | .muxctl = 0x058, 394 | .e = 0b0, 395 | .desc = "muxctrl_reg22=058 0: GPIO4_1, 1: SPI1_SDI"}, 396 | {.gpio = true, 397 | .port = 4, 398 | .pin = 2, 399 | .muxctl = 0x05C, 400 | .e = 0b0, 401 | .desc = "muxctrl_reg23=05C 0: GPIO4_2, 1: SPI1_CSN0"}, 402 | {.gpio = true, 403 | .port = 4, 404 | .pin = 3, 405 | .muxctl = 0x060, 406 | .e = 0b0, 407 | .desc = "muxctrl_reg24=060 0: GPIO4_3, 1: I2C2_SDA"}, 408 | {.gpio = true, 409 | .port = 4, 410 | .pin = 4, 411 | .muxctl = 0x064, 412 | .e = 0b0, 413 | .desc = "muxctrl_reg25=064 0: GPIO4_4, 1: I2C2_SCL"}, 414 | {.gpio = true, 415 | .port = 4, 416 | .pin = 5, 417 | .muxctl = 0x068, 418 | .e = 0b0, 419 | .desc = "muxctrl_reg26=068 0: GPIO4_5, 1: USB_OVRCUR"}, 420 | {.gpio = true, 421 | .port = 4, 422 | .pin = 6, 423 | .muxctl = 0x06C, 424 | .e = 0b0, 425 | .desc = "muxctrl_reg27=06C 0: GPIO4_6, 1: USB_PWREN"}, 426 | {.gpio = true, 427 | .port = 0, 428 | .pin = 0, 429 | .muxctl = 0x070, 430 | .e = 0b0, 431 | .desc = "muxctrl_reg28=070 000: GPIO0_0, 001: IR_IN, 010: TEMPER_DQ"}, 432 | {.gpio = true, 433 | .port = 0, 434 | .pin = 1, 435 | .muxctl = 0x074, 436 | .e = 0b0, 437 | .desc = "muxctrl_reg29=074 000: GPIO0_1, 010: TEMPER_DQ"}, 438 | {.gpio = true, 439 | .port = 0, 440 | .pin = 2, 441 | .muxctl = 0x078, 442 | .e = 0b0, 443 | .desc = "muxctrl_reg30=078 000: GPIO0_2, 010: TEMPER_DQ"}, 444 | {.gpio = true, 445 | .port = 1, 446 | .pin = 0, 447 | .muxctl = 0x07C, 448 | .e = 0b0, 449 | .desc = "muxctrl_reg31=07C 000: GPIO1_0, 001: VI_DATA13, 011: " 450 | "I2S_BCLK_TX, 100: PWM0"}, 451 | {.gpio = true, 452 | .port = 1, 453 | .pin = 1, 454 | .muxctl = 0x080, 455 | .e = 0b0, 456 | .desc = "muxctrl_reg32=080 000: GPIO1_1, 001: VI_DATA10, 011: I2S_SD_TX, " 457 | "100: UART1_TXD"}, 458 | {.gpio = true, 459 | .port = 1, 460 | .pin = 2, 461 | .muxctl = 0x080, 462 | .e = 0b0, 463 | .desc = "muxctrl_reg33=080 000: GPIO1_2, 001: VI_DATA12, 011: I2S_MCLK, " 464 | "100: UART1_CTSN"}, 465 | {.gpio = true, 466 | .port = 1, 467 | .pin = 3, 468 | .muxctl = 0x088, 469 | .e = 0b0, 470 | .desc = "muxctrl_reg34=088 000: GPIO1_3, 001: VI_DATA11, 011: I2S_WS_TX, " 471 | "100: UART2_RXD"}, 472 | {.gpio = true, 473 | .port = 1, 474 | .pin = 4, 475 | .muxctl = 0x08C, 476 | .e = 0b0, 477 | .desc = "muxctrl_reg35=08C 000: GPIO1_4, 001: VI_DATA15, 010: VI_VS_SEN, " 478 | "011: I2S_WS_RX, 100: UART1_RXD"}, 479 | {.gpio = true, 480 | .port = 1, 481 | .pin = 5, 482 | .muxctl = 0x090, 483 | .e = 0b0, 484 | .desc = "muxctrl_reg36=090 000: GPIO1_5, 001: VI_DATA14, 010: VI_HS_SEN, " 485 | "011: I2S_BCLK_RX, 100: UART1_RTSN"}, 486 | {.gpio = true, 487 | .port = 1, 488 | .pin = 6, 489 | .muxctl = 0x094, 490 | .e = 0b0, 491 | .desc = "muxctrl_reg37=094 000: GPIO1_6, 001: VI_DATA9, 011: I2S_SD_RX, " 492 | "100: UART2_TXD"}, 493 | {.gpio = true, 494 | .port = 1, 495 | .pin = 7, 496 | .muxctl = 0x098, 497 | .e = 0b0, 498 | .desc = "muxctrl_reg38=098 000: GPIO1_7, 001: SDIO0_CARD_POWER_EN"}, 499 | {.gpio = true, 500 | .port = 4, 501 | .pin = 7, 502 | .muxctl = 0x09C, 503 | .e = 0b0, 504 | .desc = "muxctrl_reg39=09C 0: GPIO4_7, 1: SDIO0_CARD_DETECT"}, 505 | {.gpio = true, 506 | .port = 5, 507 | .pin = 0, 508 | .muxctl = 0x0A0, 509 | .e = 0b0, 510 | .desc = "muxctrl_reg40=0A0 0: GPIO5_0, 1: SDIO0_CWPR"}, 511 | {.gpio = true, 512 | .port = 5, 513 | .pin = 1, 514 | .muxctl = 0x0A4, 515 | .e = 0b0, 516 | .desc = "muxctrl_reg41=0A4 0: GPIO5_1, 1: SDIO0_CCLK_OUT"}, 517 | {.gpio = true, 518 | .port = 5, 519 | .pin = 2, 520 | .muxctl = 0x0A8, 521 | .e = 0b0, 522 | .desc = "muxctrl_reg42=0A8 0: GPIO5_2, 1: SDIO0_CCMD"}, 523 | {.gpio = true, 524 | .port = 5, 525 | .pin = 3, 526 | .muxctl = 0x0AC, 527 | .e = 0b0, 528 | .desc = "muxctrl_reg43=0AC 0: GPIO5_3, 1: SDIO0_CDATA0"}, 529 | {.gpio = true, 530 | .port = 5, 531 | .pin = 4, 532 | .muxctl = 0x0B0, 533 | .e = 0b10, 534 | .desc = "muxctrl_reg44=0B0 00: TEST_CLK, 01: SDIO0_CDATA1, 10: GPIO5_4"}, 535 | {.gpio = true, 536 | .port = 5, 537 | .pin = 5, 538 | .muxctl = 0x0B4, 539 | .e = 0b0, 540 | .desc = "muxctrl_reg45=0B4 0: GPIO5_5, 1: SDIO0_CDATA2"}, 541 | {.gpio = true, 542 | .port = 5, 543 | .pin = 6, 544 | .muxctl = 0x0B8, 545 | .e = 0b0, 546 | .desc = "muxctrl_reg46=0B8 0: GPIO5_6, 1: SDIO0_CDATA3"}, 547 | {.gpio = true, 548 | .port = 5, 549 | .pin = 7, 550 | .muxctl = 0x0BC, 551 | .e = 0b0, 552 | .desc = "muxctrl_reg47=0BC 00: GPIO5_7, 01: EMMC_DATA6, 10: I2S_SD_TX, " 553 | "11: UART1_RTSN"}, 554 | {.gpio = true, 555 | .port = 6, 556 | .pin = 0, 557 | .muxctl = 0x0C0, 558 | .e = 0b0, 559 | .desc = "muxctrl_reg48=0C0 00: GPIO6_0, 01: EMMC_DATA5, 10: I2S_WS_TX, " 560 | "11: UART1_RXD"}, 561 | {.gpio = true, 562 | .port = 6, 563 | .pin = 1, 564 | .muxctl = 0x0C4, 565 | .e = 0b0, 566 | .desc = "muxctrl_reg49=0C4 00: GPIO6_1, 01: EMMC_DATA7, 10: I2S_MCLK, 11: " 567 | "UART1_CTSN"}, 568 | {.gpio = true, 569 | .port = 6, 570 | .pin = 2, 571 | .muxctl = 0x0C8, 572 | .e = 0b0, 573 | .desc = "muxctrl_reg50=0C8 00: GPIO6_2, 01: EMMC_DS, 10: I2S_SD_RX, 11: " 574 | "UART1_TXD"}, 575 | {.gpio = true, 576 | .port = 6, 577 | .pin = 3, 578 | .muxctl = 0x0CC, 579 | .e = 0b0, 580 | .desc = "muxctrl_reg51=0CC 00: GPIO6_3, 01: EMMC_DATA1, 11: UART2_RXD"}, 581 | {.gpio = true, 582 | .port = 6, 583 | .pin = 4, 584 | .muxctl = 0x0D0, 585 | .e = 0b0, 586 | .desc = "muxctrl_reg52=0D0 00: GPIO6_4, 01: EMMC_DATA2, 10: I2S_BCLK_TX, " 587 | "11: UART2_TXD"}, 588 | {.gpio = true, 589 | .port = 6, 590 | .pin = 5, 591 | .muxctl = 0x0D4, 592 | .e = 0b0, 593 | .desc = "muxctrl_reg53=0D4 00: GPIO6_5, 01: JTAG_TRSTN, 10: SPI1_CSN1, " 594 | "11: I2S_MCLK"}, 595 | {.gpio = true, 596 | .port = 6, 597 | .pin = 6, 598 | .muxctl = 0x0D8, 599 | .e = 0b0, 600 | .desc = "muxctrl_reg54=0D8 000: GPIO6_6, 001: JTAG_TCK, 010: SPI1_SCLK " 601 | ",011: I2S_WS_TX 100: I2C1_SCL"}, 602 | {.gpio = true, 603 | .port = 6, 604 | .pin = 7, 605 | .muxctl = 0x0DC, 606 | .e = 0b0, 607 | .desc = "muxctrl_reg55=0DC 00: GPIO6_7, 01: JTAG_TMS, 10: SPI1_CSN0, 11: " 608 | "I2S_SD_TX"}, 609 | {.gpio = true, 610 | .port = 7, 611 | .pin = 0, 612 | .muxctl = 0x0E0, 613 | .e = 0b0, 614 | .desc = "muxctrl_reg56=0E0 000: GPIO7_0, 001: JTAG_TDO, 010: SPI1_SDO, " 615 | "011: I2S_SD_RX, 100: I2C1_SDA"}, 616 | {.gpio = true, 617 | .port = 7, 618 | .pin = 1, 619 | .muxctl = 0x0E4, 620 | .e = 0b0, 621 | .desc = "muxctrl_reg57=0E4 00: GPIO7_1, 01: JTAG_TDI, 10: SPI1_SDI, 11: " 622 | "I2S_BCLK_TX"}, 623 | {.gpio = true, 624 | .port = 7, 625 | .pin = 2, 626 | .muxctl = 0x0E8, 627 | .e = 0b1, 628 | .desc = "muxctrl_reg58=0E8 00: PMC_PWM, 01: GPIO7_2, 10: PWM0"}, 629 | {.gpio = true, 630 | .port = 7, 631 | .pin = 3, 632 | .muxctl = 0x0EC, 633 | .e = 0b1, 634 | .desc = "muxctrl_reg59=0EC 0: PWM1, 1: GPIO7_3"}, 635 | {.gpio = true, 636 | .port = 7, 637 | .pin = 4, 638 | .muxctl = 0x0F0, 639 | .e = 0b1, 640 | .desc = "muxctrl_reg60=0F0 00: PWM2, 01: GPIO7_4, 10: SPI1_CSN1"}, 641 | {.gpio = true, 642 | .port = 7, 643 | .pin = 5, 644 | .muxctl = 0x0F4, 645 | .e = 0b1, 646 | .desc = "muxctrl_reg61=0F4 0: PWM3, 1: GPIO7_5"}, 647 | {.gpio = true, 648 | .port = 7, 649 | .pin = 6, 650 | .muxctl = 0x0F8, 651 | .e = 0b1, 652 | .desc = "muxctrl_reg62=0F8 0: SAR_ADC_CH0, 1: GPIO7_6"}, 653 | {.gpio = true, 654 | .port = 7, 655 | .pin = 7, 656 | .muxctl = 0x0FC, 657 | .e = 0b1, 658 | .desc = "muxctrl_reg63=0FC 0: SAR_ADC_CH1, 1: GPIO7_7"}, 659 | {.gpio = true, 660 | .port = 8, 661 | .pin = 0, 662 | .muxctl = 0x100, 663 | .e = 0b1, 664 | .desc = "muxctrl_reg64=100 0: SAR_ADC_CH2, 1: GPIO8_0"}, 665 | {.gpio = true, 666 | .port = 8, 667 | .pin = 1, 668 | .muxctl = 0x104, 669 | .e = 0b1, 670 | .desc = "muxctrl_reg65=104 0: SAR_ADC_CH3, 1: GPIO8_1"}, 671 | }; 672 | 673 | struct Pin *get_pins_hi3518EV200() { 674 | return hi3518EV200_pins; 675 | } 676 | uint8_t get_pins_hi3518EV200_size() { 677 | return sizeof(hi3518EV200_pins) / sizeof(hi3518EV200_pins[0]); 678 | } 679 | -------------------------------------------------------------------------------- /src/gpio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define ISP_BASE 0x20580000 7 | static const uint32_t ISP_VERSION = ISP_BASE + 0x20080; 8 | 9 | #define SC_BASE 0x20050000 10 | static const uint32_t SCSYSID0 = SC_BASE + 0x0EE0; 11 | static const uint32_t SCSYSID1 = SC_BASE + 0x0EE4; 12 | static const uint32_t SCSYSID2 = SC_BASE + 0x0EE8; 13 | static const uint32_t SCSYSID3 = SC_BASE + 0x0EEC; 14 | 15 | static const uint32_t IOMUX = 0x200F0000; 16 | 17 | static const uint32_t GPIO_DIR = 0x0400; 18 | 19 | #define GPIO0 0x20140000 20 | #define GPIO1 0x20150000 21 | #define GPIO2 0x20160000 22 | #define GPIO3 0x20170000 23 | #define GPIO4 0x20180000 24 | #define GPIO5 0x20190000 25 | #define GPIO6 0x201A0000 26 | #define GPIO7 0x201B0000 27 | #define GPIO8 0x201C0000 28 | 29 | static const uint32_t GPIO[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4, 30 | GPIO5, GPIO6, GPIO7, GPIO8}; 31 | 32 | struct Pin { 33 | uint8_t port; 34 | uint8_t pin; 35 | uint32_t muxctl; 36 | bool gpio; 37 | uint8_t e; 38 | char desc[128]; 39 | }; 40 | 41 | struct Pin *get_pins_hi3518EV200(); 42 | uint8_t get_pins_hi3518EV200_size(); 43 | 44 | bool pin_linux_to_port_pin( 45 | const uint8_t pin_number, uint8_t *port, uint8_t *pin); 46 | 47 | bool set_pin_linux(const uint8_t pin_number, const bool bit); 48 | bool get_pin_linux(const uint8_t pin_number, bool *value); 49 | -------------------------------------------------------------------------------- /src/hierrors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | char *hi_errstr(int error); 4 | -------------------------------------------------------------------------------- /src/http_post.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "mp4/mp4.h" 19 | #include "mp4/nal.h" 20 | #include 21 | #include 22 | 23 | #include "tools.h" 24 | 25 | #include "config/app_config.h" 26 | #include "http_post.h" 27 | 28 | #include "jpeg.h" 29 | 30 | #define tag "[http_post]: " 31 | 32 | HI_S32 post_send(struct JpegData *jpeg) { 33 | char *host_addr = app_config.http_post_host; 34 | 35 | int sockfd = socket(AF_INET, SOCK_STREAM, 0); 36 | if (sockfd == -1) { 37 | printf(tag "socket creation failed...\n"); 38 | return 0; 39 | } 40 | 41 | struct addrinfo *server_addr; 42 | int ret = getaddrinfo(host_addr, "80", NULL, &server_addr); 43 | if (ret == 0) { 44 | const struct addrinfo *r; 45 | for (r = server_addr; r != NULL || ret != 0; r = r->ai_next) 46 | ret = 47 | connect(sockfd, server_addr->ai_addr, server_addr->ai_addrlen); 48 | printf(tag "connected to the server '%s'..\n", host_addr); 49 | 50 | char time_url[256]; 51 | { 52 | time_t timer; 53 | time(&timer); 54 | struct tm *tm_info = localtime(&timer); 55 | size_t time_len = strftime( 56 | time_url, sizeof(time_url), app_config.http_post_url, tm_info); 57 | time_url[time_len++] = 0; 58 | } 59 | { 60 | char header_buf[1024]; 61 | int buf_len = sprintf( 62 | header_buf, 63 | "PUT %s HTTP/1.1\r\n" 64 | "Host: %s\r\n" 65 | "User-Agent: Camera openipc.org\r\n" 66 | "Accept: */*\r\n" 67 | "Content-Type: image/jpeg\r\n" 68 | "Content-Length: %u\r\n", 69 | time_url, host_addr, jpeg->jpeg_size); 70 | write(sockfd, header_buf, buf_len); 71 | 72 | if (strlen(app_config.http_post_login) > 0 && 73 | strlen(app_config.http_post_password) > 0) { 74 | char log_pass[128]; 75 | int log_pass_len = sprintf( 76 | log_pass, "%s:%s", app_config.http_post_login, 77 | app_config.http_post_password); 78 | char base64buf[1024]; 79 | int base64_len = 80 | Base64encode(base64buf, log_pass, log_pass_len); 81 | base64buf[base64_len++] = 0; 82 | int buf_len = sprintf( 83 | header_buf, "Authorization: Basic %s\r\n", base64buf); 84 | write(sockfd, header_buf, buf_len); 85 | } 86 | write(sockfd, "\r\n", 2); 87 | write(sockfd, jpeg->buf, jpeg->jpeg_size); 88 | 89 | char replay[1024]; 90 | int len = read(sockfd, replay, 1024); 91 | } 92 | 93 | close(sockfd); 94 | } 95 | freeaddrinfo(server_addr); 96 | 97 | return HI_SUCCESS; 98 | } 99 | 100 | void *send_thread(void *vargp) { 101 | struct JpegData jpeg = {0}; 102 | jpeg.buf = NULL; 103 | jpeg.buf_size = 0; 104 | jpeg.jpeg_size = 0; 105 | sleep(3); 106 | while (keepRunning) { 107 | static time_t last_time = 0; 108 | time_t current_time = time(NULL); 109 | if (current_time - last_time < app_config.http_post_interval) { 110 | sleep(1); 111 | continue; 112 | } 113 | 114 | HI_S32 s32Ret = get_jpeg( 115 | app_config.http_post_width, app_config.http_post_height, 116 | app_config.http_post_qfactor, 3, &jpeg); 117 | if (s32Ret != HI_SUCCESS) { 118 | printf(tag "get_jpeg error!\n"); 119 | continue; 120 | } 121 | last_time = current_time; 122 | 123 | s32Ret = post_send(&jpeg); 124 | if (s32Ret != HI_SUCCESS) { 125 | printf(tag "post_send error!\n"); 126 | continue; 127 | } 128 | } 129 | } 130 | 131 | void start_http_post_send() { 132 | pthread_t http_post_thread_id = 0; 133 | 134 | pthread_attr_t thread_attr; 135 | pthread_attr_init(&thread_attr); 136 | size_t stacksize; 137 | pthread_attr_getstacksize(&thread_attr, &stacksize); 138 | size_t new_stacksize = 16 * 1024; 139 | if (pthread_attr_setstacksize(&thread_attr, new_stacksize)) { 140 | printf(tag "Error: Can't set stack size %ld\n", new_stacksize); 141 | } 142 | pthread_create(&http_post_thread_id, &thread_attr, send_thread, NULL); 143 | if (pthread_attr_setstacksize(&thread_attr, stacksize)) { 144 | printf(tag "Error: Can't set stack size %ld\n", stacksize); 145 | } 146 | pthread_attr_destroy(&thread_attr); 147 | } 148 | -------------------------------------------------------------------------------- /src/http_post.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | extern bool keepRunning; 6 | 7 | void start_http_post_send(); 8 | -------------------------------------------------------------------------------- /src/jpeg.c: -------------------------------------------------------------------------------- 1 | #include "jpeg.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "compat.h" 25 | 26 | #include "videohw.h" 27 | #include "night.h" 28 | #include "config/app_config.h" 29 | #include "hierrors.h" 30 | 31 | #define tag "[hi_jpeg]: " 32 | 33 | #define MAX_WIDTH 1920 34 | #define MAX_HEIGHT 1080 35 | 36 | VENC_CHN jpeg_venc_chn; 37 | bool jpeg_module_init = false; 38 | 39 | pthread_mutex_t jpeg_mutex; 40 | 41 | int32_t InitJPEG() { 42 | pthread_mutex_lock(&jpeg_mutex); 43 | 44 | HI_S32 s32Ret; 45 | jpeg_venc_chn = take_next_free_channel(false); 46 | s32Ret = create_venc_chn(jpeg_venc_chn, -1, -1); 47 | if (HI_SUCCESS != s32Ret) { 48 | printf( 49 | tag "create_venc_chn(%d, ...) failed with %#x!\n%s\n", 50 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 51 | pthread_mutex_unlock(&jpeg_mutex); 52 | return HI_FAILURE; 53 | } 54 | 55 | VENC_ATTR_JPEG_S jpeg_attr; 56 | memset(&jpeg_attr, 0, sizeof(VENC_ATTR_JPEG_S)); 57 | jpeg_attr.u32MaxPicWidth = MAX_WIDTH; 58 | jpeg_attr.u32MaxPicHeight = MAX_HEIGHT; 59 | jpeg_attr.u32PicWidth = app_config.jpeg_width; 60 | jpeg_attr.u32PicHeight = app_config.jpeg_height; 61 | jpeg_attr.u32BufSize = 62 | (((MAX_WIDTH + 15) >> 4) << 4) * (((MAX_HEIGHT + 15) >> 4) << 4); 63 | jpeg_attr.bByFrame = 64 | HI_TRUE; /*get stream mode is field mode or frame mode*/ 65 | jpeg_attr.bSupportDCF = HI_FALSE; 66 | 67 | VENC_CHN_ATTR_S venc_chn_attr; 68 | memset(&venc_chn_attr, 0, sizeof(VENC_CHN_ATTR_S)); 69 | venc_chn_attr.stVeAttr.enType = PT_JPEG; 70 | memcpy( 71 | &venc_chn_attr.stVeAttr.JPEG_ATTR, &jpeg_attr, 72 | sizeof(VENC_ATTR_JPEG_S)); 73 | 74 | s32Ret = HI_MPI_VENC_CreateChn(jpeg_venc_chn, &venc_chn_attr); 75 | if (HI_SUCCESS != s32Ret) { 76 | printf( 77 | tag "HI_MPI_VENC_CreateChn(%d) failed with %#x!\n%s\n", 78 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 79 | pthread_mutex_unlock(&jpeg_mutex); 80 | return HI_FAILURE; 81 | } 82 | 83 | jpeg_module_init = true; 84 | unbind_vpss_venc(jpeg_venc_chn); 85 | pthread_mutex_unlock(&jpeg_mutex); 86 | printf(tag "module init ok\n"); 87 | 88 | return HI_SUCCESS; 89 | } 90 | 91 | int32_t DestroyJPEG() { 92 | pthread_mutex_lock(&jpeg_mutex); 93 | disable_venc_chn(jpeg_venc_chn); 94 | jpeg_module_init = false; 95 | pthread_mutex_unlock(&jpeg_mutex); 96 | } 97 | 98 | HI_S32 get_stream(int fd, int venc_chn, struct JpegData *jpeg_buf) { 99 | fd_set read_fds; 100 | FD_ZERO(&read_fds); 101 | FD_SET(fd, &read_fds); 102 | struct timeval TimeoutVal; 103 | TimeoutVal.tv_sec = 2; 104 | TimeoutVal.tv_usec = 0; 105 | int sel_res = select(fd + 1, &read_fds, NULL, NULL, &TimeoutVal); 106 | if (sel_res < 0) { 107 | printf(tag "(venc_chn = %d) select failed!\n", venc_chn); 108 | return HI_FAILURE; 109 | } else if (sel_res == 0) { 110 | printf(tag "get jpeg stream (chn %d) time out\n", venc_chn); 111 | return HI_FAILURE; 112 | } 113 | 114 | if (FD_ISSET(fd, &read_fds)) { 115 | VENC_CHN_STAT_S stStat; 116 | HI_S32 s32Ret = HI_MPI_VENC_Query(venc_chn, &stStat); 117 | if (HI_SUCCESS != s32Ret) { 118 | printf( 119 | tag "HI_MPI_VENC_Query(%d, ...) failed with %#x!\n%s\n", 120 | venc_chn, s32Ret, hi_errstr(s32Ret)); 121 | return HI_FAILURE; 122 | } 123 | 124 | if (0 == stStat.u32CurPacks) { 125 | printf(tag "NOTE: Current frame is NULL!\n"); 126 | return HI_FAILURE; 127 | } 128 | 129 | VENC_STREAM_S stStream; 130 | memset(&stStream, 0, sizeof(stStream)); 131 | stStream.pstPack = 132 | (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks); 133 | if (NULL == stStream.pstPack) { 134 | printf(tag "malloc stream chn[%d] pack failed!\n", venc_chn); 135 | return HI_FAILURE; 136 | } 137 | stStream.u32PackCount = stStat.u32CurPacks; 138 | s32Ret = HI_MPI_VENC_GetStream(venc_chn, &stStream, -1); 139 | if (HI_SUCCESS != s32Ret) { 140 | printf( 141 | tag "HI_MPI_VENC_GetStream(%d, ...) failed with %#x!\n%s\n", 142 | venc_chn, s32Ret, hi_errstr(s32Ret)); 143 | free(stStream.pstPack); 144 | stStream.pstPack = NULL; 145 | return HI_FAILURE; 146 | } 147 | { 148 | jpeg_buf->jpeg_size = 0; 149 | for (HI_U32 i = 0; i < stStream.u32PackCount; i++) { 150 | VENC_PACK_S *pack = &stStream.pstPack[i]; 151 | uint32_t pack_len = pack->u32Len - pack->u32Offset; 152 | uint8_t *pack_data = pack->pu8Addr + pack->u32Offset; 153 | 154 | ssize_t need_size = jpeg_buf->jpeg_size + pack_len; 155 | if (need_size > jpeg_buf->buf_size) { 156 | jpeg_buf->buf = realloc(jpeg_buf->buf, need_size); 157 | jpeg_buf->buf_size = need_size; 158 | } 159 | memcpy( 160 | jpeg_buf->buf + jpeg_buf->jpeg_size, pack_data, pack_len); 161 | jpeg_buf->jpeg_size += pack_len; 162 | } 163 | } 164 | 165 | s32Ret = HI_MPI_VENC_ReleaseStream(venc_chn, &stStream); 166 | if (HI_SUCCESS != s32Ret) { 167 | printf( 168 | tag "HI_MPI_VENC_ReleaseStream(%d, ...) failed with %#x!\n%s\n", 169 | venc_chn, s32Ret, hi_errstr(s32Ret)); 170 | free(stStream.pstPack); 171 | stStream.pstPack = NULL; 172 | return HI_FAILURE; 173 | } 174 | free(stStream.pstPack); 175 | stStream.pstPack = NULL; 176 | } 177 | 178 | return HI_SUCCESS; 179 | } 180 | 181 | int32_t request_pic( 182 | uint32_t width, uint32_t height, uint32_t qfactor, uint8_t color2Grey, 183 | struct JpegData *jpeg_buf) { 184 | HI_S32 s32Ret; 185 | bind_vpss_venc(jpeg_venc_chn); 186 | 187 | VENC_ATTR_JPEG_S jpeg_attr; 188 | memset(&jpeg_attr, 0, sizeof(VENC_ATTR_JPEG_S)); 189 | jpeg_attr.u32MaxPicWidth = MAX_WIDTH; 190 | jpeg_attr.u32MaxPicHeight = MAX_HEIGHT; 191 | jpeg_attr.u32PicWidth = width; 192 | jpeg_attr.u32PicHeight = height; 193 | jpeg_attr.u32BufSize = 194 | (((MAX_WIDTH + 15) >> 4) << 4) * (((MAX_HEIGHT + 15) >> 4) << 4); 195 | jpeg_attr.bByFrame = 196 | HI_TRUE; /*get stream mode is field mode or frame mode*/ 197 | jpeg_attr.bSupportDCF = HI_FALSE; 198 | 199 | VENC_CHN_ATTR_S venc_chn_attr; 200 | memset(&venc_chn_attr, 0, sizeof(VENC_CHN_ATTR_S)); 201 | venc_chn_attr.stVeAttr.enType = PT_JPEG; 202 | memcpy( 203 | &venc_chn_attr.stVeAttr.JPEG_ATTR, &jpeg_attr, 204 | sizeof(VENC_ATTR_JPEG_S)); 205 | 206 | s32Ret = HI_MPI_VENC_SetChnAttr(jpeg_venc_chn, &venc_chn_attr); 207 | if (HI_SUCCESS != s32Ret) { 208 | printf( 209 | tag "HI_MPI_VENC_SetChnAttr(%d, ...) failed with %#x!\n%s\n", 210 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 211 | return HI_FAILURE; 212 | } 213 | 214 | VENC_PARAM_JPEG_S venc_jpeg_param; 215 | memset(&venc_jpeg_param, 0, sizeof(VENC_PARAM_JPEG_S)); 216 | s32Ret = HI_MPI_VENC_GetJpegParam(jpeg_venc_chn, &venc_jpeg_param); 217 | if (HI_SUCCESS != s32Ret) { 218 | printf( 219 | tag "HI_MPI_VENC_SetJpegParam(%d, ...) failed with %#x!\n%s\n", 220 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 221 | return HI_FAILURE; 222 | } 223 | venc_jpeg_param.u32Qfactor = qfactor; 224 | s32Ret = HI_MPI_VENC_SetJpegParam(jpeg_venc_chn, &venc_jpeg_param); 225 | if (HI_SUCCESS != s32Ret) { 226 | printf( 227 | tag "HI_MPI_VENC_SetJpegParam(%d, ...) failed with %#x!\n%s\n", 228 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 229 | return HI_FAILURE; 230 | } 231 | 232 | VENC_COLOR2GREY_S pstChnColor2Grey; 233 | if (color2Grey == 0) { 234 | pstChnColor2Grey.bColor2Grey = false; 235 | } else if (color2Grey == 1) { 236 | pstChnColor2Grey.bColor2Grey = true; 237 | } else 238 | pstChnColor2Grey.bColor2Grey = night_mode_is_enable(); 239 | s32Ret = HI_MPI_VENC_SetColor2Grey(jpeg_venc_chn, &pstChnColor2Grey); 240 | if (HI_SUCCESS != s32Ret) { 241 | printf( 242 | tag "HI_MPI_VENC_CreateChn(%d) failed with %#x!\n%s\n", 243 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 244 | } 245 | 246 | VENC_RECV_PIC_PARAM_S pstRecvParam; 247 | pstRecvParam.s32RecvPicNum = 1; 248 | s32Ret = HI_MPI_VENC_StartRecvPicEx(jpeg_venc_chn, &pstRecvParam); 249 | if (HI_SUCCESS != s32Ret) { 250 | printf( 251 | tag "HI_MPI_VENC_StartRecvPicEx(%d, ...) failed with %#x!\n%s\n", 252 | jpeg_venc_chn, s32Ret, hi_errstr(s32Ret)); 253 | return HI_FAILURE; 254 | } 255 | 256 | HI_S32 fd = HI_MPI_VENC_GetFd(jpeg_venc_chn); 257 | HI_S32 stream_err = get_stream(fd, jpeg_venc_chn, jpeg_buf); 258 | if (HI_MPI_VENC_CloseFd(jpeg_venc_chn) != HI_SUCCESS) { 259 | printf(tag "HI_MPI_VENC_CloseFd(%d) fail\n", jpeg_venc_chn); 260 | }; 261 | 262 | s32Ret = HI_MPI_VENC_StopRecvPic(jpeg_venc_chn); 263 | if (HI_SUCCESS != s32Ret) { 264 | printf( 265 | "HI_MPI_VENC_StopRecvPic(%d) failed with %#x!\n%s\n", jpeg_venc_chn, 266 | s32Ret, hi_errstr(s32Ret)); /* return EXIT_FAILURE; */ 267 | } 268 | 269 | unbind_vpss_venc(jpeg_venc_chn); 270 | return stream_err; 271 | } 272 | 273 | int32_t get_jpeg( 274 | uint32_t width, uint32_t height, uint32_t qfactor, uint8_t color2gray, 275 | struct JpegData *jpeg_buf) { 276 | pthread_mutex_lock(&jpeg_mutex); 277 | if (!jpeg_module_init) { 278 | pthread_mutex_unlock(&jpeg_mutex); 279 | printf(tag "module is not enable\n"); 280 | return HI_FAILURE; 281 | } 282 | HI_S32 s32Ret = request_pic(width, height, qfactor, color2gray, jpeg_buf); 283 | if (s32Ret != HI_SUCCESS) { 284 | printf(tag "Can't request_pic!\n"); 285 | } 286 | pthread_mutex_unlock(&jpeg_mutex); 287 | return s32Ret; 288 | } 289 | -------------------------------------------------------------------------------- /src/jpeg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct JpegData { 7 | char *buf; 8 | ssize_t buf_size; 9 | ssize_t jpeg_size; 10 | }; 11 | 12 | int32_t InitJPEG(); 13 | int32_t DestroyJPEG(); 14 | int32_t get_jpeg( 15 | uint32_t width, uint32_t height, uint32_t qfactor, uint8_t color2Grey, 16 | struct JpegData *jpeg_buf); 17 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "videohw.h" 9 | #include "server.h" 10 | 11 | #include "config/app_config.h" 12 | 13 | #include "rtsp/ringfifo.h" 14 | #include "rtsp/rtputils.h" 15 | #include "rtsp/rtspservice.h" 16 | 17 | #include "http_post.h" 18 | 19 | #include "night.h" 20 | 21 | int main(int argc, char *argv[]) { 22 | if (parse_app_config("./mini.ini") != CONFIG_OK) { 23 | printf("Can't load app config './mini.ini'\n"); 24 | return EXIT_FAILURE; 25 | } 26 | 27 | start_server(); 28 | 29 | memset(&state, 0, sizeof(struct SDKState)); 30 | 31 | int s32MainFd; 32 | if (app_config.rtsp_enable) { 33 | ringmalloc(1920 * 1080); 34 | printf("RTSP server START, listen for client connecting...\n"); 35 | PrefsInit(); 36 | signal(SIGINT, IntHandl); 37 | s32MainFd = tcp_listen(SERVER_RTSP_PORT_DEFAULT); 38 | if (ScheduleInit() == ERR_FATAL) { 39 | fprintf( 40 | stderr, 41 | "Fatal: Can't start scheduler %s, %i \nServer is aborting.\n", 42 | __FILE__, __LINE__); 43 | return 0; 44 | } 45 | RTP_port_pool_init(RTP_DEFAULT_PORT); 46 | } 47 | 48 | if (start_sdk(&state) == EXIT_FAILURE) 49 | return EXIT_FAILURE; 50 | 51 | if (app_config.night_mode_enable) 52 | start_monitor_light_sensor(); 53 | if (app_config.http_post_enable) 54 | start_http_post_send(); 55 | if (app_config.rtsp_enable) { 56 | struct timespec ts = {2, 0}; 57 | while (keepRunning) { 58 | nanosleep(&ts, NULL); 59 | EventLoop(s32MainFd); 60 | } 61 | ringfree(); 62 | printf("RTSP server quit!\n"); 63 | } else { 64 | while (keepRunning) 65 | sleep(1); 66 | } 67 | 68 | stop_sdk(&state); 69 | stop_server(); 70 | 71 | printf("Shutdown main thread\n"); 72 | return EXIT_SUCCESS; 73 | } 74 | -------------------------------------------------------------------------------- /src/mmap.h: -------------------------------------------------------------------------------- 1 | #ifndef MMAP_H 2 | #define MMAP_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef BROKEN_MMAP 8 | #define PROT_READ 0x1 /* Page can be read. */ 9 | #define PROT_WRITE 0x2 /* Page can be written. */ 10 | #define PROT_EXEC 0x4 /* Page can be executed. */ 11 | 12 | #define MAP_SHARED 0x01 13 | #define MAP_PRIVATE 0x02 14 | #define MAP_FAILED ((void *)-1) 15 | 16 | void *mmap(void *start, size_t len, int prot, int flags, int fd, uint32_t off); 17 | int munmap(void *__addr, size_t __len); 18 | 19 | #else 20 | #include 21 | #endif 22 | 23 | #endif /* MMAP_H */ 24 | -------------------------------------------------------------------------------- /src/motion_detect.c: -------------------------------------------------------------------------------- 1 | #include "motion_detect.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "hierrors.h" 19 | #include "server.h" 20 | 21 | typedef struct hiSAMPLE_IVE_RECT_S { 22 | POINT_S astPoint[4]; 23 | } SAMPLE_IVE_RECT_S; 24 | 25 | typedef struct hiSAMPLE_RECT_ARRAY_S { 26 | HI_U16 u16Num; 27 | SAMPLE_IVE_RECT_S astRect[50]; 28 | } SAMPLE_RECT_ARRAY_S; 29 | 30 | #define SAMPLE_IVE_MD_IMAGE_NUM 2 31 | 32 | typedef struct hiSAMPLE_IVE_MD_S { 33 | IVE_SRC_IMAGE_S astImg[SAMPLE_IVE_MD_IMAGE_NUM]; 34 | IVE_DST_MEM_INFO_S stBlob; 35 | MD_ATTR_S stMdAttr; 36 | SAMPLE_RECT_ARRAY_S stRegion; 37 | VB_POOL hVbPool; 38 | HI_U16 u16BaseWitdh; 39 | HI_U16 u16BaseHeight; 40 | } SAMPLE_IVE_MD_S; 41 | 42 | HI_VOID IVE_Md_Uninit(SAMPLE_IVE_MD_S *pstMd); 43 | HI_S32 IVE_Md_Init( 44 | SAMPLE_IVE_MD_S *pstMd, HI_U16 u16ExtWidth, HI_U16 u16ExtHeight, 45 | HI_U16 u16BaseWidth, HI_U16 u16BaseHeight); 46 | HI_S32 DmaImage( 47 | VIDEO_FRAME_INFO_S *pstFrameInfo, IVE_DST_IMAGE_S *pstDst, bool instant); 48 | HI_S32 IVE_CreateImage( 49 | IVE_IMAGE_S *pstImg, IVE_IMAGE_TYPE_E enType, HI_U16 u16Width, 50 | HI_U16 u16Height); 51 | HI_S32 IVE_CreateMemInfo(IVE_MEM_INFO_S *pstMemInfo, HI_U32 u32Size); 52 | 53 | int motion_detect_init() { 54 | HI_S32 s32Ret = HI_IVS_MD_Init(); 55 | if (HI_SUCCESS != s32Ret) { 56 | printf( 57 | "HI_IVS_MD_Init failed with %#x!\n%s\n", s32Ret, hi_errstr(s32Ret)); 58 | } 59 | return s32Ret; 60 | } 61 | 62 | int motion_detect_run(HI_VOID *pArgs) { 63 | MD_CHN md_chn = 0; 64 | MD_ATTR_S md_attr; 65 | md_attr.enAlgMode = MD_ALG_MODE_REF; 66 | memset(&md_attr, 0, sizeof(md_attr)); 67 | HI_S32 s32Ret = HI_IVS_MD_CreateChn(md_chn, &md_attr); 68 | if (HI_SUCCESS != s32Ret) { 69 | printf( 70 | "HI_IVS_MD_CreateChn failed with %#x!\n%s\n", s32Ret, 71 | hi_errstr(s32Ret)); 72 | return EXIT_FAILURE; 73 | } 74 | 75 | VIDEO_FRAME_INFO_S stBaseFrmInfo; 76 | VIDEO_FRAME_INFO_S stExtFrmInfo; 77 | HI_S32 s32GetFrameMilliSec = 2000; 78 | VI_CHN viBaseChn = 0; 79 | VI_CHN viExtChn = 1; 80 | 81 | bool first_frame = true; 82 | bool instant = true; 83 | 84 | HI_S32 s32CurIdx = 0; 85 | 86 | HI_U16 u16ExtWidth = 352; 87 | HI_U16 u16ExtHeight = 288; 88 | HI_U16 u16BaseWidth = 1920; 89 | HI_U16 u16BaseHeight = 1080; 90 | 91 | SAMPLE_IVE_MD_S stMd; 92 | 93 | s32Ret = IVE_Md_Init( 94 | &stMd, u16ExtWidth, u16ExtHeight, u16BaseWidth, u16BaseHeight); 95 | 96 | MD_ATTR_S *pstMdAttr = &(stMd.stMdAttr); 97 | 98 | while (keepRunning) { 99 | s32Ret = 100 | HI_MPI_VI_GetFrame(viExtChn, &stExtFrmInfo, s32GetFrameMilliSec); 101 | if (HI_SUCCESS != s32Ret) { 102 | printf( 103 | "HI_MPI_VI_GetFrame failed with %#x!\n%s\n", s32Ret, 104 | hi_errstr(s32Ret)); 105 | } 106 | s32Ret = 107 | HI_MPI_VI_GetFrame(viBaseChn, &stBaseFrmInfo, s32GetFrameMilliSec); 108 | if (HI_SUCCESS != s32Ret) { 109 | printf( 110 | "HI_MPI_VI_GetFrame failed with %#x!\n%s\n", s32Ret, 111 | hi_errstr(s32Ret)); 112 | } 113 | 114 | if (!first_frame) { 115 | s32Ret = DmaImage(&stExtFrmInfo, &stMd.astImg[s32CurIdx], instant); 116 | if (HI_SUCCESS != s32Ret) { 117 | printf( 118 | "DmaImage failed with %#x!\n%s\n", s32Ret, 119 | hi_errstr(s32Ret)); 120 | goto BASE_RELEASE; 121 | } 122 | } else { 123 | s32Ret = 124 | DmaImage(&stExtFrmInfo, &stMd.astImg[1 - s32CurIdx], instant); 125 | if (HI_SUCCESS != s32Ret) { 126 | printf( 127 | "DmaImage failed with %#x!\n%s\n", s32Ret, 128 | hi_errstr(s32Ret)); 129 | goto BASE_RELEASE; 130 | } 131 | first_frame = false; 132 | goto CHANGE_IDX; 133 | } 134 | 135 | s32Ret = HI_IVS_MD_Process( 136 | md_chn, &stMd.astImg[s32CurIdx], &stMd.astImg[1 - s32CurIdx], NULL, 137 | &stMd.stBlob); 138 | if (HI_SUCCESS != s32Ret) { 139 | printf( 140 | "HI_IVS_MD_Process failed with %#x!\n%s\n", s32Ret, 141 | hi_errstr(s32Ret)); 142 | goto BASE_RELEASE; 143 | } 144 | 145 | CHANGE_IDX: 146 | s32CurIdx = 1 - s32CurIdx; 147 | 148 | BASE_RELEASE: 149 | s32Ret = HI_MPI_VI_ReleaseFrame(viBaseChn, &stBaseFrmInfo); 150 | if (HI_SUCCESS != s32Ret) { 151 | printf( 152 | "HI_MPI_VI_ReleaseFrame failed with %#x!\n%s\n", s32Ret, 153 | hi_errstr(s32Ret)); 154 | } 155 | 156 | EXT_RELEASE: 157 | s32Ret = HI_MPI_VI_ReleaseFrame(viExtChn, &stExtFrmInfo); 158 | if (HI_SUCCESS != s32Ret) { 159 | printf( 160 | "HI_MPI_VI_ReleaseFrame failed with %#x!\n%s\n", s32Ret, 161 | hi_errstr(s32Ret)); 162 | } 163 | } 164 | } 165 | 166 | int motion_detect_deinit() { 167 | HI_S32 s32Ret = HI_IVS_MD_Exit(); 168 | if (HI_SUCCESS != s32Ret) { 169 | printf( 170 | "HI_IVS_MD_Exit failed with %#x!\n%s\n", s32Ret, hi_errstr(s32Ret)); 171 | } 172 | return s32Ret; 173 | } 174 | 175 | HI_S32 DmaImage( 176 | VIDEO_FRAME_INFO_S *pstFrameInfo, IVE_DST_IMAGE_S *pstDst, bool instant) { 177 | HI_S32 s32Ret; 178 | IVE_HANDLE hIveHandle; 179 | IVE_SRC_DATA_S stSrcData; 180 | IVE_DST_DATA_S stDstData; 181 | IVE_DMA_CTRL_S stCtrl = {IVE_DMA_MODE_DIRECT_COPY, 0}; 182 | HI_BOOL bFinish = HI_FALSE; 183 | HI_BOOL bBlock = HI_TRUE; 184 | 185 | // fill src 186 | stSrcData.pu8VirAddr = (HI_U8 *)pstFrameInfo->stVFrame.pVirAddr[0]; 187 | stSrcData.u32PhyAddr = pstFrameInfo->stVFrame.u32PhyAddr[0]; 188 | stSrcData.u16Width = (HI_U16)pstFrameInfo->stVFrame.u32Width; 189 | stSrcData.u16Height = (HI_U16)pstFrameInfo->stVFrame.u32Height; 190 | stSrcData.u16Stride = (HI_U16)pstFrameInfo->stVFrame.u32Stride[0]; 191 | 192 | // fill dst 193 | stDstData.pu8VirAddr = pstDst->pu8VirAddr[0]; 194 | stDstData.u32PhyAddr = pstDst->u32PhyAddr[0]; 195 | stDstData.u16Width = pstDst->u16Width; 196 | stDstData.u16Height = pstDst->u16Height; 197 | stDstData.u16Stride = pstDst->u16Stride[0]; 198 | 199 | s32Ret = 200 | HI_MPI_IVE_DMA(&hIveHandle, &stSrcData, &stDstData, &stCtrl, instant); 201 | if (HI_SUCCESS != s32Ret) { 202 | printf( 203 | "HI_MPI_IVE_DMA failed with %#x!\n%s\n", s32Ret, hi_errstr(s32Ret)); 204 | return s32Ret; 205 | } 206 | 207 | if (instant) { 208 | s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock); 209 | while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret) { 210 | usleep(100); 211 | s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock); 212 | } 213 | if (HI_SUCCESS != s32Ret) { 214 | printf( 215 | "HI_MPI_IVE_Query failed with %#x!\n%s\n", s32Ret, 216 | hi_errstr(s32Ret)); 217 | return s32Ret; 218 | } 219 | } 220 | return HI_SUCCESS; 221 | } 222 | 223 | HI_S32 IVE_Md_Init( 224 | SAMPLE_IVE_MD_S *pstMd, HI_U16 u16ExtWidth, HI_U16 u16ExtHeight, 225 | HI_U16 u16BaseWidth, HI_U16 u16BaseHeight) { 226 | memset(pstMd, 0, sizeof(SAMPLE_IVE_MD_S)); 227 | for (uint32_t i = 0; i < SAMPLE_IVE_MD_IMAGE_NUM; i++) { 228 | HI_S32 s32Ret = IVE_CreateImage( 229 | &pstMd->astImg[i], IVE_IMAGE_TYPE_U8C1, u16ExtWidth, u16ExtHeight); 230 | if (HI_SUCCESS != s32Ret) { 231 | printf( 232 | "SAMPLE_COMM_IVE_CreateImage failed with %#x!\n%s\n", s32Ret, 233 | hi_errstr(s32Ret)); 234 | goto MD_INIT_FAIL; 235 | } 236 | } 237 | HI_U32 u32Size = sizeof(IVE_CCBLOB_S); 238 | HI_S32 s32Ret = IVE_CreateMemInfo(&pstMd->stBlob, u32Size); 239 | if (HI_SUCCESS != s32Ret) { 240 | printf( 241 | "SAMPLE_COMM_IVE_CreateMemInfo failed with %#x!\n%s\n", s32Ret, 242 | hi_errstr(s32Ret)); 243 | goto MD_INIT_FAIL; 244 | } 245 | 246 | pstMd->u16BaseWitdh = u16BaseWidth; 247 | pstMd->u16BaseHeight = u16BaseHeight; 248 | // Set attr info 249 | pstMd->stMdAttr.enAlgMode = MD_ALG_MODE_BG; 250 | pstMd->stMdAttr.enSadMode = IVE_SAD_MODE_MB_4X4; 251 | pstMd->stMdAttr.enSadOutCtrl = IVE_SAD_OUT_CTRL_THRESH; 252 | pstMd->stMdAttr.u16SadThr = 100 * (1 << 1); // 100 * (1 << 2); 253 | pstMd->stMdAttr.u16Width = u16ExtWidth; 254 | pstMd->stMdAttr.u16Height = u16ExtHeight; 255 | pstMd->stMdAttr.stAddCtrl.u0q16X = 32768; 256 | pstMd->stMdAttr.stAddCtrl.u0q16Y = 32768; 257 | HI_U8 u8WndSz = (1 << (2 + pstMd->stMdAttr.enSadMode)); 258 | pstMd->stMdAttr.stCclCtrl.u16InitAreaThr = u8WndSz * u8WndSz; 259 | pstMd->stMdAttr.stCclCtrl.u16Step = u8WndSz; 260 | 261 | s32Ret = HI_IVS_MD_Init(); 262 | if (HI_SUCCESS != s32Ret) { 263 | printf( 264 | "HI_IVS_MD_Init failed with %#x!\n%s\n", s32Ret, hi_errstr(s32Ret)); 265 | goto MD_INIT_FAIL; 266 | } 267 | 268 | MD_INIT_FAIL: 269 | if (HI_SUCCESS != s32Ret) { 270 | IVE_Md_Uninit(pstMd); 271 | } 272 | return s32Ret; 273 | } 274 | 275 | HI_U16 IVE_CalcStride(HI_U16 u16Width, HI_U8 u8Align) { 276 | return (u16Width + (u8Align - u16Width % u8Align) % u8Align); 277 | } 278 | 279 | #define IVE_ALIGN 16 280 | HI_S32 IVE_CreateImage( 281 | IVE_IMAGE_S *pstImg, IVE_IMAGE_TYPE_E enType, HI_U16 u16Width, 282 | HI_U16 u16Height) { 283 | HI_U32 u32Size = 0; 284 | HI_S32 s32Ret; 285 | if (NULL == pstImg) { 286 | printf("pstImg is null\n"); 287 | return HI_FAILURE; 288 | } 289 | 290 | pstImg->enType = enType; 291 | pstImg->u16Width = u16Width; 292 | pstImg->u16Height = u16Height; 293 | pstImg->u16Stride[0] = IVE_CalcStride(pstImg->u16Width, IVE_ALIGN); 294 | 295 | switch (enType) { 296 | case IVE_IMAGE_TYPE_U8C1: 297 | case IVE_IMAGE_TYPE_S8C1: { 298 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height; 299 | s32Ret = HI_MPI_SYS_MmzAlloc( 300 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 301 | HI_NULL, u32Size); 302 | if (HI_SUCCESS != s32Ret) { 303 | printf( 304 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 305 | hi_errstr(s32Ret)); 306 | return s32Ret; 307 | } 308 | } break; 309 | case IVE_IMAGE_TYPE_YUV420SP: { 310 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height * 3 / 2; 311 | s32Ret = HI_MPI_SYS_MmzAlloc( 312 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 313 | HI_NULL, u32Size); 314 | if (HI_SUCCESS != s32Ret) { 315 | printf( 316 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 317 | hi_errstr(s32Ret)); 318 | return s32Ret; 319 | } 320 | pstImg->u16Stride[1] = pstImg->u16Stride[0]; 321 | pstImg->u32PhyAddr[1] = 322 | pstImg->u32PhyAddr[0] + pstImg->u16Stride[0] * pstImg->u16Height; 323 | pstImg->pu8VirAddr[1] = 324 | pstImg->pu8VirAddr[0] + pstImg->u16Stride[0] * pstImg->u16Height; 325 | 326 | } break; 327 | case IVE_IMAGE_TYPE_YUV422SP: { 328 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height * 2; 329 | s32Ret = HI_MPI_SYS_MmzAlloc( 330 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 331 | HI_NULL, u32Size); 332 | if (HI_SUCCESS != s32Ret) { 333 | printf( 334 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 335 | hi_errstr(s32Ret)); 336 | return s32Ret; 337 | } 338 | pstImg->u16Stride[1] = pstImg->u16Stride[0]; 339 | pstImg->u32PhyAddr[1] = 340 | pstImg->u32PhyAddr[0] + pstImg->u16Stride[0] * pstImg->u16Height; 341 | pstImg->pu8VirAddr[1] = 342 | pstImg->pu8VirAddr[0] + pstImg->u16Stride[0] * pstImg->u16Height; 343 | 344 | } break; 345 | case IVE_IMAGE_TYPE_YUV420P: 346 | break; 347 | case IVE_IMAGE_TYPE_YUV422P: 348 | break; 349 | case IVE_IMAGE_TYPE_S8C2_PACKAGE: 350 | break; 351 | case IVE_IMAGE_TYPE_S8C2_PLANAR: 352 | break; 353 | case IVE_IMAGE_TYPE_S16C1: 354 | case IVE_IMAGE_TYPE_U16C1: { 355 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height * sizeof(HI_U16); 356 | s32Ret = HI_MPI_SYS_MmzAlloc( 357 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 358 | HI_NULL, u32Size); 359 | if (HI_SUCCESS != s32Ret) { 360 | printf( 361 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 362 | hi_errstr(s32Ret)); 363 | return s32Ret; 364 | } 365 | } break; 366 | case IVE_IMAGE_TYPE_U8C3_PACKAGE: { 367 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height * 3; 368 | s32Ret = HI_MPI_SYS_MmzAlloc( 369 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 370 | HI_NULL, u32Size); 371 | if (HI_SUCCESS != s32Ret) { 372 | printf( 373 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 374 | hi_errstr(s32Ret)); 375 | return s32Ret; 376 | } 377 | pstImg->pu8VirAddr[1] = pstImg->pu8VirAddr[0] + 1; 378 | pstImg->pu8VirAddr[2] = pstImg->pu8VirAddr[1] + 1; 379 | pstImg->u32PhyAddr[1] = pstImg->u32PhyAddr[0] + 1; 380 | pstImg->u32PhyAddr[2] = pstImg->u32PhyAddr[1] + 1; 381 | pstImg->u16Stride[1] = pstImg->u16Stride[0]; 382 | pstImg->u16Stride[2] = pstImg->u16Stride[0]; 383 | } break; 384 | case IVE_IMAGE_TYPE_U8C3_PLANAR: 385 | break; 386 | case IVE_IMAGE_TYPE_S32C1: 387 | case IVE_IMAGE_TYPE_U32C1: { 388 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height * sizeof(HI_U32); 389 | s32Ret = HI_MPI_SYS_MmzAlloc( 390 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 391 | HI_NULL, u32Size); 392 | if (HI_SUCCESS != s32Ret) { 393 | printf( 394 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 395 | hi_errstr(s32Ret)); 396 | return s32Ret; 397 | } 398 | } break; 399 | case IVE_IMAGE_TYPE_S64C1: 400 | case IVE_IMAGE_TYPE_U64C1: { 401 | u32Size = pstImg->u16Stride[0] * pstImg->u16Height * sizeof(HI_U64); 402 | s32Ret = HI_MPI_SYS_MmzAlloc( 403 | &pstImg->u32PhyAddr[0], (void **)&pstImg->pu8VirAddr[0], NULL, 404 | HI_NULL, u32Size); 405 | if (HI_SUCCESS != s32Ret) { 406 | printf( 407 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 408 | hi_errstr(s32Ret)); 409 | return s32Ret; 410 | } 411 | } break; 412 | default: 413 | break; 414 | } 415 | return HI_SUCCESS; 416 | } 417 | 418 | HI_S32 IVE_CreateMemInfo(IVE_MEM_INFO_S *pstMemInfo, HI_U32 u32Size) { 419 | if (NULL == pstMemInfo) { 420 | printf("pstMemInfo is null\n"); 421 | return HI_FAILURE; 422 | } 423 | pstMemInfo->u32Size = u32Size; 424 | HI_S32 s32Ret = HI_MPI_SYS_MmzAlloc( 425 | &pstMemInfo->u32PhyAddr, (void **)&pstMemInfo->pu8VirAddr, NULL, 426 | HI_NULL, u32Size); 427 | if (HI_SUCCESS != s32Ret) { 428 | printf( 429 | "HI_MPI_SYS_MmzAlloc failed with %#x!\n%s\n", s32Ret, 430 | hi_errstr(s32Ret)); 431 | return s32Ret; 432 | } 433 | return HI_SUCCESS; 434 | } 435 | 436 | // free mmz 437 | #define IVE_MMZ_FREE(phy, vir) \ 438 | do { \ 439 | if ((0 != (phy)) && (NULL != (vir))) { \ 440 | HI_MPI_SYS_MmzFree((phy), (vir)); \ 441 | (phy) = 0; \ 442 | (vir) = NULL; \ 443 | } \ 444 | } while (0) 445 | 446 | HI_VOID IVE_Md_Uninit(SAMPLE_IVE_MD_S *pstMd) { 447 | for (HI_S32 i = 0; i < SAMPLE_IVE_MD_IMAGE_NUM; i++) { 448 | IVE_MMZ_FREE( 449 | pstMd->astImg[i].u32PhyAddr[0], pstMd->astImg[i].pu8VirAddr[0]); 450 | } 451 | IVE_MMZ_FREE(pstMd->stBlob.u32PhyAddr, pstMd->stBlob.pu8VirAddr); 452 | HI_S32 s32Ret = HI_IVS_MD_Exit(); 453 | if (HI_SUCCESS != s32Ret) { 454 | printf( 455 | "HI_IVS_MD_Exit failed with %#x!\n%s\n", s32Ret, hi_errstr(s32Ret)); 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /src/motion_detect.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | int motion_detect_init(); 4 | int start(); 5 | int motion_detect_deinit(); 6 | -------------------------------------------------------------------------------- /src/mp4/bitbuf.c: -------------------------------------------------------------------------------- 1 | #include "bitbuf.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #define chk_ptr \ 7 | if (!ptr) \ 8 | return BUF_INCORRECT; 9 | #define chk_realloc \ 10 | { \ 11 | enum BufError err; \ 12 | err = try_to_realloc(ptr, pos); \ 13 | if (err != BUF_OK) \ 14 | return err; \ 15 | } 16 | 17 | char *buf_error_to_str(const enum BufError err) { 18 | switch (err) { 19 | case BUF_OK: 20 | return "BUF_OK"; 21 | case BUF_ENDOFBUF_ERROR: 22 | return "BUF_ENDOFBUF_ERROR"; 23 | case BUF_MALLOC_ERROR: 24 | return "BUF_MALLOC_ERROR"; 25 | case BUF_INCORRECT: 26 | return "BUF_INCORRECT"; 27 | default: { 28 | static char str[32]; 29 | sprintf(str, "Unknown(%d)\0", err); 30 | return str; 31 | } 32 | } 33 | } 34 | 35 | enum BufError try_to_realloc(struct BitBuf *ptr, const uint32_t min_size) { 36 | chk_ptr uint32_t new_size = ptr->size + min_size + 1024; 37 | char *new_buf = realloc(ptr->buf, new_size); 38 | if (new_buf == NULL) 39 | return BUF_MALLOC_ERROR; 40 | ptr->buf = new_buf; 41 | ptr->size = new_size; 42 | return BUF_OK; 43 | } 44 | 45 | enum BufError put_skip(struct BitBuf *ptr, const uint32_t count) { 46 | chk_ptr uint32_t pos = ptr->offset + count; 47 | if (pos >= ptr->size) 48 | chk_realloc for (uint32_t i = 0; i < count; i++) 49 | ptr->buf[ptr->offset + i] = 0; 50 | ptr->offset = pos; 51 | return BUF_OK; 52 | } 53 | 54 | enum BufError put_to_offset( 55 | struct BitBuf *ptr, const uint32_t offset, const char *data, 56 | const uint32_t size) { 57 | chk_ptr uint32_t pos = offset + size; 58 | if (pos >= ptr->size) 59 | chk_realloc for (uint32_t i = 0; i < size; i++) ptr->buf[offset + i] = 60 | data[i]; 61 | return BUF_OK; 62 | } 63 | enum BufError put(struct BitBuf *ptr, const char *data, const uint32_t size) { 64 | chk_ptr enum BufError err = put_to_offset(ptr, ptr->offset, data, size); 65 | chk_err ptr->offset += size; 66 | return BUF_OK; 67 | } 68 | 69 | enum BufError 70 | put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val) { 71 | chk_ptr uint32_t pos = offset + sizeof(uint8_t); 72 | if (pos >= ptr->size) 73 | chk_realloc ptr->buf[offset + 0] = val & 0xff; 74 | return BUF_OK; 75 | } 76 | enum BufError put_u8(struct BitBuf *ptr, uint8_t val) { 77 | chk_ptr enum BufError err = put_u8_to_offset(ptr, ptr->offset, val); 78 | chk_err ptr->offset += sizeof(uint8_t); 79 | return BUF_OK; 80 | } 81 | 82 | enum BufError put_u16_be_to_offset( 83 | struct BitBuf *ptr, const uint32_t offset, const uint16_t val) { 84 | chk_ptr uint32_t pos = offset + sizeof(uint16_t); 85 | if (pos >= ptr->size) 86 | chk_realloc ptr->buf[offset + 0] = (val >> 8) & 0xff; 87 | ptr->buf[offset + 1] = (val >> 0) & 0xff; 88 | return BUF_OK; 89 | } 90 | enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val) { 91 | chk_ptr enum BufError err = put_u16_be_to_offset(ptr, ptr->offset, val); 92 | chk_err ptr->offset += sizeof(uint16_t); 93 | return BUF_OK; 94 | } 95 | 96 | enum BufError put_u16_le_to_offset( 97 | struct BitBuf *ptr, const uint32_t offset, const uint16_t val) { 98 | chk_ptr uint32_t pos = offset + sizeof(uint16_t); 99 | if (pos >= ptr->size) 100 | chk_realloc ptr->buf[offset + 0] = (val >> 0) & 0xff; 101 | ptr->buf[offset + 1] = (val >> 8) & 0xff; 102 | return BUF_OK; 103 | } 104 | enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val) { 105 | chk_ptr enum BufError err = put_u16_le_to_offset(ptr, ptr->offset, val); 106 | chk_err ptr->offset += sizeof(uint16_t); 107 | return BUF_OK; 108 | } 109 | 110 | enum BufError put_u32_be_to_offset( 111 | struct BitBuf *ptr, const uint32_t offset, const uint32_t val) { 112 | chk_ptr uint32_t pos = offset + sizeof(uint32_t); 113 | if (pos >= ptr->size) 114 | chk_realloc ptr->buf[offset + 0] = (val >> 24) & 0xff; 115 | ptr->buf[offset + 1] = (val >> 16) & 0xff; 116 | ptr->buf[offset + 2] = (val >> 8) & 0xff; 117 | ptr->buf[offset + 3] = (val >> 0) & 0xff; 118 | return BUF_OK; 119 | } 120 | enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val) { 121 | chk_ptr enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); 122 | chk_err ptr->offset += sizeof(uint32_t); 123 | return BUF_OK; 124 | } 125 | enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val) { 126 | chk_ptr enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); 127 | chk_err ptr->offset += sizeof(int32_t); 128 | return BUF_OK; 129 | } 130 | 131 | enum BufError put_u64_be_to_offset( 132 | struct BitBuf *ptr, const uint32_t offset, const uint64_t val) { 133 | chk_ptr uint32_t pos = offset + sizeof(uint64_t); 134 | if (pos > ptr->size) 135 | chk_realloc ptr->buf[offset + 0] = (val >> 56) & 0xff; 136 | ptr->buf[offset + 1] = (val >> 48) & 0xff; 137 | ptr->buf[offset + 2] = (val >> 40) & 0xff; 138 | ptr->buf[offset + 3] = (val >> 32) & 0xff; 139 | ptr->buf[offset + 4] = (val >> 24) & 0xff; 140 | ptr->buf[offset + 5] = (val >> 16) & 0xff; 141 | ptr->buf[offset + 6] = (val >> 8) & 0xff; 142 | ptr->buf[offset + 7] = (val >> 0) & 0xff; 143 | return BUF_OK; 144 | } 145 | enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val) { 146 | chk_ptr enum BufError err = put_u64_be_to_offset(ptr, ptr->offset, val); 147 | chk_err ptr->offset += sizeof(uint64_t); 148 | return BUF_OK; 149 | } 150 | 151 | enum BufError put_u32_le_to_offset( 152 | struct BitBuf *ptr, const uint32_t offset, const uint32_t val) { 153 | chk_ptr uint32_t pos = offset + 4; 154 | if (pos >= ptr->size) 155 | chk_realloc ptr->buf[offset + 0] = (val >> 0) & 0xff; 156 | ptr->buf[offset + 1] = (val >> 8) & 0xff; 157 | ptr->buf[offset + 2] = (val >> 16) & 0xff; 158 | ptr->buf[offset + 3] = (val >> 24) & 0xff; 159 | return BUF_OK; 160 | } 161 | enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val) { 162 | chk_ptr enum BufError err = put_u32_le_to_offset(ptr, ptr->offset, val); 163 | chk_err ptr->offset += sizeof(uint32_t); 164 | return BUF_OK; 165 | } 166 | 167 | enum BufError put_str4_to_offset( 168 | struct BitBuf *ptr, const uint32_t offset, const char str[4]) { 169 | chk_ptr uint32_t pos = offset + 4; 170 | if (pos >= ptr->size) 171 | chk_realloc for (uint8_t i = 0; i < 4; i++) { 172 | ptr->buf[offset + i] = str[i]; 173 | } 174 | return BUF_OK; 175 | } 176 | enum BufError put_str4(struct BitBuf *ptr, const char str[4]) { 177 | chk_ptr enum BufError err = put_str4_to_offset(ptr, ptr->offset, str); 178 | chk_err ptr->offset += 4; 179 | return BUF_OK; 180 | } 181 | enum BufError put_counted_str_to_offset( 182 | struct BitBuf *ptr, const uint32_t offset, const char *str, 183 | const uint32_t len) { 184 | chk_ptr uint32_t pos = offset + len + 1; 185 | if (pos >= ptr->size) 186 | chk_realloc for (uint32_t i = 0; i < len + 1; i++) { 187 | ptr->buf[offset + i] = str[i]; 188 | } 189 | ptr->buf[pos] = 0; 190 | return BUF_OK; 191 | } 192 | enum BufError 193 | put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len) { 194 | chk_ptr enum BufError err = 195 | put_counted_str_to_offset(ptr, ptr->offset, str, len); 196 | chk_err ptr->offset += len + 1; 197 | return BUF_OK; 198 | } 199 | -------------------------------------------------------------------------------- /src/mp4/bitbuf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #define chk_err \ 7 | if (err != BUF_OK) { \ 8 | printf( \ 9 | "Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), \ 10 | __func__, __FILE__, __LINE__); \ 11 | return err; \ 12 | } 13 | #define chk_err_continue \ 14 | if (err != BUF_OK) { \ 15 | printf( \ 16 | "Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), \ 17 | __func__, __FILE__, __LINE__); \ 18 | continue; \ 19 | } 20 | 21 | enum BufError { 22 | BUF_OK = 0, 23 | BUF_ENDOFBUF_ERROR, 24 | BUF_MALLOC_ERROR, 25 | BUF_INCORRECT 26 | }; 27 | char *buf_error_to_str(const enum BufError err); 28 | 29 | struct BitBuf { 30 | char *buf; 31 | uint32_t size; 32 | uint32_t offset; 33 | }; 34 | 35 | enum BufError put_skip(struct BitBuf *ptr, const uint32_t count); 36 | enum BufError put_to_offset( 37 | struct BitBuf *ptr, const uint32_t offset, const char *data, 38 | const uint32_t size); 39 | enum BufError put(struct BitBuf *ptr, const char *data, const uint32_t size); 40 | enum BufError 41 | put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val); 42 | enum BufError put_u8(struct BitBuf *ptr, uint8_t val); 43 | enum BufError put_u16_be_to_offset( 44 | struct BitBuf *ptr, const uint32_t offset, const uint16_t val); 45 | enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val); 46 | enum BufError put_u16_le_to_offset( 47 | struct BitBuf *ptr, const uint32_t offset, const uint16_t val); 48 | enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val); 49 | enum BufError put_u32_be_to_offset( 50 | struct BitBuf *ptr, const uint32_t offset, const uint32_t val); 51 | enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val); 52 | enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val); 53 | enum BufError put_u64_be_to_offset( 54 | struct BitBuf *ptr, const uint32_t offset, const uint64_t val); 55 | enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val); 56 | enum BufError put_u32_le_to_offset( 57 | struct BitBuf *ptr, const uint32_t offset, const uint32_t val); 58 | enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val); 59 | enum BufError put_str4_to_offset( 60 | struct BitBuf *ptr, const uint32_t offset, const char str[4]); 61 | enum BufError put_str4(struct BitBuf *ptr, const char str[4]); 62 | enum BufError put_counted_str_to_offset( 63 | struct BitBuf *ptr, const uint32_t offset, const char *str, 64 | const uint32_t len); 65 | enum BufError 66 | put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len); 67 | -------------------------------------------------------------------------------- /src/mp4/moof.c: -------------------------------------------------------------------------------- 1 | #include "moof.h" 2 | #include 3 | 4 | uint32_t pos_sequence_number = 0; 5 | uint32_t pos_base_data_offset = 0; 6 | uint32_t pos_base_media_decode_time = 0; 7 | 8 | struct DataOffsetPos { 9 | bool data_offset_present; 10 | uint32_t offset; 11 | }; 12 | 13 | enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number); 14 | enum BufError write_traf( 15 | struct BitBuf *ptr, const uint32_t sequence_number, 16 | const uint64_t base_data_offset, const uint64_t base_media_decode_time, 17 | const uint32_t default_sample_duration, 18 | const struct SampleInfo *samples_info, const uint32_t samples_info_len, 19 | struct DataOffsetPos *data_offset); 20 | enum BufError write_tfhd( 21 | struct BitBuf *ptr, const uint32_t sequence_number, 22 | const uint64_t base_data_offset, const uint64_t base_media_decode_time, 23 | const uint32_t default_sample_size, const uint32_t default_sample_duration, 24 | const struct SampleInfo *samples_info, const uint32_t samples_info_len, 25 | struct DataOffsetPos *data_offset); 26 | enum BufError 27 | write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time); 28 | enum BufError write_trun( 29 | struct BitBuf *ptr, const struct SampleInfo *samples_info, 30 | const uint32_t samples_info_count, struct DataOffsetPos *data_offset); 31 | 32 | enum BufError 33 | write_mdat(struct BitBuf *ptr, const char *data, const uint32_t len) { 34 | enum BufError err; 35 | uint32_t start_atom = ptr->offset; 36 | err = put_u32_be(ptr, 0); 37 | chk_err; 38 | err = put_str4(ptr, "mdat"); 39 | chk_err; 40 | err = put_u32_be(ptr, len); 41 | chk_err; 42 | err = put(ptr, data, len); 43 | chk_err; 44 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 45 | chk_err; 46 | return BUF_OK; 47 | } 48 | 49 | enum BufError write_moof( 50 | struct BitBuf *ptr, const uint32_t sequence_number, 51 | const uint64_t base_data_offset, const uint64_t base_media_decode_time, 52 | const uint32_t default_sample_duration, 53 | const struct SampleInfo *samples_info, const uint32_t samples_info_len) { 54 | enum BufError err; 55 | uint32_t start_atom = ptr->offset; 56 | err = put_u32_be(ptr, 0); 57 | chk_err; 58 | err = put_str4(ptr, "moof"); 59 | chk_err; 60 | err = write_mfhd(ptr, sequence_number); 61 | chk_err; 62 | 63 | struct DataOffsetPos data_offset; 64 | data_offset.offset = 0; 65 | err = write_traf( 66 | ptr, sequence_number, base_data_offset, base_media_decode_time, 67 | default_sample_duration, samples_info, samples_info_len, &data_offset); 68 | chk_err; 69 | if (data_offset.data_offset_present) 70 | err = put_u32_be_to_offset( 71 | ptr, data_offset.offset, 72 | ptr->offset + 4 /*mdat size*/ + 4 /*mdat id*/); 73 | chk_err; 74 | 75 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 76 | chk_err; 77 | return BUF_OK; 78 | } 79 | 80 | enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number) { 81 | enum BufError err; 82 | uint32_t start_atom = ptr->offset; 83 | err = put_u32_be(ptr, 0); 84 | chk_err; 85 | err = put_str4(ptr, "mfhd"); 86 | chk_err err = put_u8(ptr, 0); // 1 version 87 | err = put_u8(ptr, 0); 88 | chk_err; 89 | err = put_u8(ptr, 0); 90 | chk_err; 91 | err = put_u8(ptr, 0); 92 | chk_err; 93 | // 3 flags 94 | pos_sequence_number = ptr->offset; 95 | err = put_u32_be(ptr, sequence_number); 96 | chk_err; // 4 sequence_number 97 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 98 | chk_err; 99 | return BUF_OK; 100 | } 101 | 102 | enum BufError write_traf( 103 | struct BitBuf *ptr, const uint32_t sequence_number, 104 | const uint64_t base_data_offset, const uint64_t base_media_decode_time, 105 | const uint32_t default_sample_duration, 106 | const struct SampleInfo *samples_info, const uint32_t samples_info_len, 107 | struct DataOffsetPos *data_offset) { 108 | enum BufError err; 109 | uint32_t start_atom = ptr->offset; 110 | err = put_u32_be(ptr, 0); 111 | chk_err; 112 | err = put_str4(ptr, "traf"); 113 | chk_err; 114 | err = write_tfhd( 115 | ptr, sequence_number, base_data_offset, base_media_decode_time, 116 | samples_info[0].size, default_sample_duration, samples_info, 117 | samples_info_len, data_offset); 118 | chk_err; 119 | err = write_tfdt(ptr, base_media_decode_time); 120 | chk_err; 121 | err = write_trun(ptr, samples_info, samples_info_len, data_offset); 122 | chk_err; 123 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 124 | chk_err; 125 | return BUF_OK; 126 | } 127 | 128 | enum BufError write_tfhd( 129 | struct BitBuf *ptr, const uint32_t sequence_number, 130 | const uint64_t base_data_offset, const uint64_t base_media_decode_time, 131 | const uint32_t default_sample_size, const uint32_t default_sample_duration, 132 | const struct SampleInfo *samples_info, const uint32_t samples_info_len, 133 | struct DataOffsetPos *data_offset) { 134 | enum BufError err; 135 | uint32_t start_atom = ptr->offset; 136 | err = put_u32_be(ptr, 0); 137 | chk_err; 138 | err = put_str4(ptr, "tfhd"); 139 | chk_err; 140 | 141 | err = put_u8(ptr, 0); 142 | chk_err; // 1 byte version 143 | uint64_t flags = 0x0; 144 | const bool base_data_offset_present = false; 145 | const bool sample_description_index_present = false; 146 | const bool default_sample_duration_present = true; 147 | const bool default_sample_size_present = true; 148 | const bool default_sample_flags_present = true; 149 | const bool duration_is_empty = false; 150 | const bool default_base_is_moof = false; 151 | 152 | if (base_data_offset_present) { 153 | flags = flags | 0x000001; 154 | } // base-data-offset-present 155 | if (sample_description_index_present) { 156 | flags = flags | 0x000002; 157 | } // sample-description-index-present 158 | if (default_sample_duration_present) { 159 | flags = flags | 0x000008; 160 | } // default-sample-duration-present 161 | if (default_sample_size_present) { 162 | flags = flags | 0x000010; 163 | } // default-sample-size-present 164 | if (default_sample_flags_present) { 165 | flags = flags | 0x000020; 166 | } // default-sample-flags-present 167 | if (duration_is_empty) { 168 | flags = flags | 0x010000; 169 | } // duration-is-empty 170 | if (default_base_is_moof) { 171 | flags = flags | 0x020000; 172 | } // default-base-is-moof 173 | err = put_u8(ptr, flags >> 16); 174 | chk_err; 175 | err = put_u8(ptr, flags >> 8); 176 | chk_err; 177 | err = put_u8(ptr, flags >> 0); 178 | chk_err; // 3 flags 179 | 180 | err = put_u32_be(ptr, 1); 181 | chk_err; // 4 track_ID 182 | if (base_data_offset_present) { 183 | pos_base_data_offset = ptr->offset; 184 | err = put_u64_be(ptr, base_data_offset); 185 | chk_err; 186 | } 187 | // if sample_description_index_present { buf.put_u32_be(0); } // 4 188 | // default_sample_description_index 189 | if (default_sample_duration_present) { 190 | err = put_u32_be(ptr, default_sample_duration); 191 | chk_err; 192 | } 193 | if (default_sample_size_present) { 194 | err = put_u32_be(ptr, default_sample_size); 195 | chk_err; 196 | } 197 | if (default_sample_flags_present) { 198 | err = put_u32_be(ptr, 16842752); 199 | chk_err; 200 | } 201 | 202 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 203 | chk_err; 204 | return BUF_OK; 205 | } 206 | 207 | enum BufError 208 | write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time) { 209 | enum BufError err; 210 | uint32_t start_atom = ptr->offset; 211 | err = put_u32_be(ptr, 0); 212 | chk_err; 213 | err = put_str4(ptr, "tfdt"); 214 | chk_err err = put_u8(ptr, 1); // 1 version 215 | err = put_u8(ptr, 0); 216 | chk_err; 217 | err = put_u8(ptr, 0); 218 | chk_err; 219 | err = put_u8(ptr, 0); 220 | chk_err; // 3 flags 221 | pos_base_media_decode_time = ptr->offset; 222 | err = put_u64_be(ptr, base_media_decode_time); 223 | chk_err; // 4 baseMediaDecodeTime 224 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 225 | chk_err; 226 | return BUF_OK; 227 | } 228 | 229 | enum BufError write_trun( 230 | struct BitBuf *ptr, const struct SampleInfo *samples_info, 231 | const uint32_t samples_info_count, struct DataOffsetPos *data_offset) { 232 | enum BufError err; 233 | uint32_t start_atom = ptr->offset; 234 | err = put_u32_be(ptr, 0); 235 | chk_err; 236 | err = put_str4(ptr, "trun"); 237 | chk_err; 238 | 239 | err = put_u8(ptr, 0); 240 | chk_err; // 1 version 241 | const bool data_offset_present = true; 242 | const bool first_sample_flags_present = false; 243 | const bool sample_duration_present = true; 244 | const bool sample_size_present = true; 245 | const bool sample_flags_present = true; 246 | const bool sample_composition_time_offsets_present = true; 247 | { 248 | uint64_t flags = 0x0; 249 | if (data_offset_present) { 250 | flags = flags | 0x000001; 251 | } // 0x000001 data-offset-present. 252 | if (first_sample_flags_present) { 253 | flags = flags | 0x000004; 254 | } // 0x000004 first-sample-flags-present 255 | if (sample_duration_present) { 256 | flags = flags | 0x000100; 257 | } // 0x000100 sample-duration-present 258 | if (sample_size_present) { 259 | flags = flags | 0x000200; 260 | } // 0x000200 sample-size-present 261 | if (sample_flags_present) { 262 | flags = flags | 0x000400; 263 | } // 0x000400 sample-flags-present 264 | if (sample_composition_time_offsets_present) { 265 | flags = flags | 0x000800; 266 | } // 0x000800 sample-composition-time-offsets-present 267 | err = put_u8(ptr, flags >> 16); 268 | chk_err; 269 | err = put_u8(ptr, flags >> 8); 270 | chk_err; 271 | err = put_u8(ptr, flags >> 0); 272 | chk_err // 3 flags 273 | } 274 | err = put_u32_be(ptr, samples_info_count); 275 | chk_err; // 4 sample_count 276 | 277 | data_offset->data_offset_present = data_offset_present; 278 | data_offset->offset = 279 | ptr->offset; // save pointer to this place. we will change size after 280 | // moof atom will created 281 | if (data_offset_present) { 282 | err = put_i32_be(ptr, 0); 283 | chk_err; 284 | } // 4 fake data_offset 285 | 286 | if (first_sample_flags_present) { 287 | err = put_u32_be(ptr, 33554432); 288 | chk_err; 289 | } // 4 first_sample_flags 290 | for (uint32_t i = 0; i < samples_info_count; ++i) { 291 | const struct SampleInfo sample_info = samples_info[i]; 292 | if (sample_duration_present) { 293 | err = put_u32_be(ptr, sample_info.duration); 294 | chk_err; 295 | } // 4 sample_duration 296 | if (sample_size_present) { 297 | err = put_u32_be(ptr, sample_info.size); 298 | chk_err; 299 | } // 4 sample_size 300 | if (sample_flags_present) { 301 | err = put_u32_be(ptr, sample_info.flags); 302 | chk_err; 303 | } // 4 sample_flags 304 | if (sample_composition_time_offsets_present) { 305 | err = put_i32_be(ptr, sample_info.composition_offset); 306 | chk_err; 307 | } 308 | } 309 | 310 | err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); 311 | chk_err; 312 | return BUF_OK; 313 | } 314 | -------------------------------------------------------------------------------- /src/mp4/moof.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bitbuf.h" 3 | 4 | extern uint32_t pos_sequence_number; 5 | extern uint32_t pos_base_data_offset; 6 | extern uint32_t pos_base_media_decode_time; 7 | 8 | struct SampleInfo { 9 | uint32_t duration; 10 | uint32_t decode_time; 11 | uint32_t composition_time; 12 | uint32_t composition_offset; 13 | uint32_t size; 14 | uint32_t flags; 15 | }; 16 | 17 | enum BufError 18 | write_mdat(struct BitBuf *ptr, const char *data, const uint32_t len); 19 | enum BufError write_moof( 20 | struct BitBuf *ptr, const uint32_t sequence_number, 21 | const uint64_t base_data_offset, const uint64_t base_media_decode_time, 22 | const uint32_t default_sample_duration, 23 | const struct SampleInfo *samples_info, const uint32_t samples_info_len); 24 | -------------------------------------------------------------------------------- /src/mp4/moov.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "bitbuf.h" 3 | 4 | struct MoovInfo { 5 | uint8_t profile_idc; 6 | uint8_t level_idc; 7 | char *sps; 8 | uint16_t sps_length; 9 | char *pps; 10 | uint16_t pps_length; 11 | uint16_t width; 12 | uint16_t height; 13 | uint32_t horizontal_resolution; 14 | uint32_t vertical_resolution; 15 | uint32_t creation_time; 16 | uint32_t timescale; 17 | }; 18 | 19 | enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info); 20 | -------------------------------------------------------------------------------- /src/mp4/mp4.c: -------------------------------------------------------------------------------- 1 | #include "mp4.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "config/app_config.h" 8 | #include "config/sensor_config.h" 9 | 10 | uint32_t default_sample_size = 40000; 11 | 12 | enum BufError create_header(); 13 | 14 | void set_sps( 15 | struct Mp4Context *ctx, const char *nal_data, const uint32_t nal_len) { 16 | memcpy(ctx->buf_sps, nal_data, nal_len); 17 | ctx->buf_sps_len = nal_len; 18 | create_header(ctx); 19 | } 20 | 21 | void set_pps( 22 | struct Mp4Context *ctx, const char *nal_data, const uint32_t nal_len) { 23 | memcpy(ctx->buf_pps, nal_data, nal_len); 24 | ctx->buf_pps_len = nal_len; 25 | create_header(ctx); 26 | } 27 | 28 | enum BufError create_header(struct Mp4Context *ctx) { 29 | if (ctx->buf_header.offset > 0) 30 | return BUF_OK; 31 | if (ctx->buf_sps_len == 0) 32 | return BUF_OK; 33 | if (ctx->buf_pps_len == 0) 34 | return BUF_OK; 35 | 36 | struct MoovInfo moov_info; 37 | memset(&moov_info, 0, sizeof(struct MoovInfo)); 38 | moov_info.profile_idc = 100; 39 | moov_info.level_idc = 41; 40 | moov_info.width = sensor_config.isp.isp_w; 41 | moov_info.height = sensor_config.isp.isp_h; 42 | moov_info.horizontal_resolution = 0x00480000; // 72 dpi 43 | moov_info.vertical_resolution = 0x00480000; // 72 dpi 44 | moov_info.creation_time = 0; 45 | moov_info.timescale = 46 | default_sample_size * sensor_config.isp.isp_frame_rate; 47 | moov_info.sps = ctx->buf_sps; 48 | moov_info.sps_length = ctx->buf_sps_len; 49 | moov_info.pps = ctx->buf_pps; 50 | moov_info.pps_length = ctx->buf_pps_len; 51 | 52 | ctx->buf_header.offset = 0; 53 | enum BufError err = write_header(&ctx->buf_header, &moov_info); 54 | chk_err return BUF_OK; 55 | } 56 | 57 | enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr) { 58 | ptr->buf = ctx->buf_header.buf; 59 | ptr->size = ctx->buf_header.size; 60 | ptr->offset = ctx->buf_header.offset; 61 | return BUF_OK; 62 | } 63 | 64 | enum BufError set_slice( 65 | struct Mp4Context *ctx, const char *nal_data, const uint32_t nal_len, 66 | const enum NalUnitType unit_type) { 67 | enum BufError err; 68 | 69 | const uint32_t samples_info_len = 1; 70 | struct SampleInfo samples_info[1]; 71 | memset(&samples_info[0], 0, sizeof(struct SampleInfo)); 72 | samples_info[0].size = nal_len + 4; // add size of sample 73 | samples_info[0].composition_offset = default_sample_size; 74 | samples_info[0].decode_time = default_sample_size; 75 | samples_info[0].duration = default_sample_size; 76 | samples_info[0].flags = unit_type == NalUnitType_CodedSliceIdr ? 0 : 65536; 77 | 78 | ctx->buf_moof.offset = 0; 79 | err = write_moof( 80 | &ctx->buf_moof, 0, 0, 0, default_sample_size, samples_info, 81 | samples_info_len); 82 | chk_err 83 | 84 | ctx->buf_mdat.offset = 0; 85 | 86 | err = write_mdat(&ctx->buf_mdat, nal_data, nal_len); 87 | chk_err 88 | 89 | return BUF_OK; 90 | } 91 | 92 | enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state) { 93 | enum BufError err; 94 | if (pos_sequence_number > 0) 95 | err = put_u32_be_to_offset( 96 | &ctx->buf_moof, pos_sequence_number, state->sequence_number); 97 | chk_err if (pos_base_data_offset > 0) err = put_u64_be_to_offset( 98 | &ctx->buf_moof, pos_base_data_offset, state->base_data_offset); 99 | chk_err if (pos_base_media_decode_time > 0) err = put_u64_be_to_offset( 100 | &ctx->buf_moof, pos_base_media_decode_time, 101 | state->base_media_decode_time); 102 | chk_err state->sequence_number++; 103 | state->base_data_offset += ctx->buf_moof.offset + ctx->buf_mdat.offset; 104 | state->base_media_decode_time += state->default_sample_duration; 105 | return BUF_OK; 106 | } 107 | enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr) { 108 | ptr->buf = ctx->buf_moof.buf; 109 | ptr->size = ctx->buf_moof.size; 110 | ptr->offset = ctx->buf_moof.offset; 111 | return BUF_OK; 112 | } 113 | enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr) { 114 | ptr->buf = ctx->buf_mdat.buf; 115 | ptr->size = ctx->buf_mdat.size; 116 | ptr->offset = ctx->buf_mdat.offset; 117 | return BUF_OK; 118 | } 119 | -------------------------------------------------------------------------------- /src/mp4/mp4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "bitbuf.h" 6 | #include "moof.h" 7 | #include "moov.h" 8 | #include "nal.h" 9 | 10 | extern uint32_t default_sample_size; 11 | 12 | struct Mp4State { 13 | bool header_sent; 14 | 15 | uint32_t sequence_number; 16 | uint64_t base_data_offset; 17 | uint64_t base_media_decode_time; 18 | uint32_t default_sample_duration; 19 | 20 | uint32_t nals_count; 21 | }; 22 | 23 | struct Mp4Context { 24 | char buf_sps[128]; 25 | uint16_t buf_sps_len; 26 | char buf_pps[128]; 27 | uint16_t buf_pps_len; 28 | 29 | struct BitBuf buf_header; 30 | struct BitBuf buf_moof; 31 | struct BitBuf buf_mdat; 32 | }; 33 | 34 | enum BufError set_slice( 35 | struct Mp4Context *ctx, const char *nal_data, const uint32_t nal_len, 36 | const enum NalUnitType unit_type); 37 | void set_sps( 38 | struct Mp4Context *ctx, const char *nal_data, const uint32_t nal_len); 39 | void set_pps( 40 | struct Mp4Context *ctx, const char *nal_data, const uint32_t nal_len); 41 | 42 | enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr); 43 | 44 | enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state); 45 | enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr); 46 | enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr); 47 | -------------------------------------------------------------------------------- /src/mp4/nal.c: -------------------------------------------------------------------------------- 1 | #include "nal.h" 2 | 3 | char *nal_type_to_str(const enum NalUnitType nal_type) { 4 | switch (nal_type) { 5 | case NalUnitType_Unspecified: 6 | return "Unspecified"; 7 | case NalUnitType_CodedSliceNonIdr: 8 | return "CodedSliceNonIdr"; 9 | case NalUnitType_CodedSliceDataPartitionA: 10 | return "CodedSliceDataPartitionA"; 11 | case NalUnitType_CodedSliceDataPartitionB: 12 | return "CodedSliceDataPartitionB"; 13 | case NalUnitType_CodedSliceDataPartitionC: 14 | return "CodedSliceDataPartitionC"; 15 | case NalUnitType_CodedSliceIdr: 16 | return "CodedSliceIdr"; 17 | case NalUnitType_SEI: 18 | return "SEI"; 19 | case NalUnitType_SPS: 20 | return "SPS"; 21 | case NalUnitType_PPS: 22 | return "PPS"; 23 | case NalUnitType_AUD: 24 | return "AUD"; 25 | case NalUnitType_EndOfSequence: 26 | return "EndOfSequence"; 27 | case NalUnitType_EndOfStream: 28 | return "EndOfStream"; 29 | case NalUnitType_Filler: 30 | return "Filler"; 31 | case NalUnitType_SpsExt: 32 | return "SpsExt"; 33 | case NalUnitType_CodedSliceAux: 34 | return "CodedSliceAux"; 35 | default: 36 | return "Unknown"; 37 | } 38 | } 39 | 40 | void nal_parse_header(struct NAL *nal, const char first_byte) { 41 | nal->forbidden_zero_bit = ((first_byte & 0b10000000) >> 7) == 1; 42 | nal->ref_idc = (first_byte & 0b01100000) >> 5; 43 | nal->unit_type = (first_byte & 0b00011111) >> 0; 44 | } 45 | 46 | bool nal_chk4(const char *buf, const uint32_t offset) { 47 | if (buf[offset] == 0x00 && buf[offset + 1] == 0x00 && 48 | buf[offset + 2] == 0x01) { 49 | return true; 50 | } 51 | if (buf[offset] == 0x00 && buf[offset + 1] == 0x00 && 52 | buf[offset + 2] == 0x00 && buf[offset + 3] == 0x01) { 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | bool nal_chk3(const char *buf, const uint32_t offset) { 59 | if (buf[offset] == 0x00 && buf[offset + 1] == 0x00 && 60 | buf[offset + 2] == 0x01) { 61 | return true; 62 | } 63 | return false; 64 | } 65 | -------------------------------------------------------------------------------- /src/mp4/nal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | enum NalUnitType { // Table 7-1 NAL unit type codes 6 | NalUnitType_Unspecified = 0, // Unspecified 7 | NalUnitType_CodedSliceNonIdr = 1, // Coded slice of a non-IDR picture 8 | NalUnitType_CodedSliceDataPartitionA = 2, // Coded slice data partition A 9 | NalUnitType_CodedSliceDataPartitionB = 3, // Coded slice data partition B 10 | NalUnitType_CodedSliceDataPartitionC = 4, // Coded slice data partition C 11 | NalUnitType_CodedSliceIdr = 5, // Coded slice of an IDR picture 12 | NalUnitType_SEI = 6, // Supplemental enhancement information (SEI) 13 | NalUnitType_SPS = 7, // Sequence parameter set 14 | NalUnitType_PPS = 8, // Picture parameter set 15 | NalUnitType_AUD = 9, // Access unit delimiter 16 | NalUnitType_EndOfSequence = 10, // End of sequence 17 | NalUnitType_EndOfStream = 11, // End of stream 18 | NalUnitType_Filler = 12, // Filler data 19 | NalUnitType_SpsExt = 13, // Sequence parameter set extension 20 | // 14..18 // Reserved 21 | NalUnitType_CodedSliceAux = 22 | 19, // Coded slice of an auxiliary coded picture without partitioning 23 | // 20..23 // Reserved 24 | // 24..31 // Unspecified 25 | }; 26 | 27 | char *nal_type_to_str(const enum NalUnitType nal_type); 28 | 29 | struct NAL { 30 | char *data; 31 | uint64_t data_size; 32 | uint32_t picture_order_count; 33 | 34 | // NAL header 35 | bool forbidden_zero_bit; 36 | uint8_t ref_idc; 37 | uint8_t unit_type_value; 38 | enum NalUnitType unit_type; 39 | }; 40 | 41 | void nal_parse_header(struct NAL *nal, const char first_byte); 42 | bool nal_chk4(const char *buf, const uint32_t offset); 43 | bool nal_chk3(const char *buf, const uint32_t offset); 44 | -------------------------------------------------------------------------------- /src/night.c: -------------------------------------------------------------------------------- 1 | #include "night.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "config/app_config.h" 12 | #include "videohw.h" 13 | #include "compat.h" 14 | 15 | #include "gpio.h" 16 | 17 | #define tag "[night]: " 18 | 19 | static bool night_mode = false; 20 | 21 | bool night_mode_is_enable() { return night_mode; } 22 | 23 | void ircut_on() { 24 | set_pin_linux(app_config.ir_cut_pin1, false); 25 | set_pin_linux(app_config.ir_cut_pin2, true); 26 | usleep(app_config.pin_switch_delay_us); 27 | set_pin_linux(app_config.ir_cut_pin1, false); 28 | set_pin_linux(app_config.ir_cut_pin2, false); 29 | set_color2gray(true); 30 | } 31 | 32 | void ircut_off() { 33 | set_pin_linux(app_config.ir_cut_pin1, true); 34 | set_pin_linux(app_config.ir_cut_pin2, false); 35 | usleep(app_config.pin_switch_delay_us); 36 | set_pin_linux(app_config.ir_cut_pin1, false); 37 | set_pin_linux(app_config.ir_cut_pin2, false); 38 | set_color2gray(false); 39 | } 40 | 41 | void set_night_mode(bool night) { 42 | if (night) { 43 | printf("Change mode to NIGHT\n"); 44 | ircut_off(); 45 | set_color2gray(true); 46 | } else { 47 | printf("Change mode to DAY\n"); 48 | ircut_on(); 49 | set_color2gray(false); 50 | } 51 | } 52 | 53 | extern bool keepRunning; 54 | 55 | void *night_thread_func(void *vargp) { 56 | usleep(1000); 57 | set_night_mode(night_mode); 58 | 59 | while (keepRunning) { 60 | bool state = false; 61 | if (!get_pin_linux(app_config.ir_sensor_pin, &state)) { 62 | printf(tag "get_pin_linux(app_config.ir_sensor_pin) error\n"); 63 | sleep(app_config.check_interval_s); 64 | continue; 65 | } 66 | if (night_mode != state) { 67 | night_mode = state; 68 | set_night_mode(night_mode); 69 | } 70 | sleep(app_config.check_interval_s); 71 | } 72 | } 73 | 74 | int32_t start_monitor_light_sensor() { 75 | #if HISILICON_SDK_GEN == 2 76 | pthread_t thread_id = 0; 77 | 78 | pthread_attr_t thread_attr; 79 | pthread_attr_init(&thread_attr); 80 | size_t stacksize; 81 | pthread_attr_getstacksize(&thread_attr, &stacksize); 82 | size_t new_stacksize = 16 * 1024; 83 | if (pthread_attr_setstacksize(&thread_attr, new_stacksize)) { 84 | printf(tag "Error: Can't set stack size %ld\n", new_stacksize); 85 | } 86 | pthread_create(&thread_id, &thread_attr, night_thread_func, NULL); 87 | if (pthread_attr_setstacksize(&thread_attr, stacksize)) { 88 | printf(tag "Error: Can't set stack size %ld\n", stacksize); 89 | } 90 | pthread_attr_destroy(&thread_attr); 91 | #endif 92 | } 93 | -------------------------------------------------------------------------------- /src/night.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | bool night_mode_is_enable(); 7 | 8 | int32_t start_monitor_light_sensor(); 9 | -------------------------------------------------------------------------------- /src/rtsp/ringfifo.c: -------------------------------------------------------------------------------- 1 | /*ringbuf .c*/ 2 | 3 | #include "ringfifo.h" 4 | #include "rtputils.h" 5 | #include "rtspservice.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define NMAX 32 12 | 13 | int iput = 0; /* 环形缓冲区的当前放入位置 */ 14 | int iget = 0; /* 缓冲区的当前取出位置 */ 15 | int n = 0; /* 环形缓冲区中的元素总数量 */ 16 | 17 | struct ringbuf ringfifo[NMAX]; 18 | extern int UpdateSpsOrPps(unsigned char *data, int frame_type, int len); 19 | 20 | /* 环形缓冲区的地址编号计算函数,如果到达唤醒缓冲区的尾部,将绕回到头部。 21 | 环形缓冲区的有效地址编号为:0到(NMAX-1) 22 | */ 23 | //分配环形缓冲区,总共32个,每个大小为size 24 | void ringmalloc(int size) { 25 | int i; 26 | for (i = 0; i < NMAX; i++) { 27 | ringfifo[i].buffer = malloc(size); 28 | ringfifo[i].size = 0; 29 | ringfifo[i].frame_type = 0; 30 | } 31 | iput = 0; /* 环形缓冲区的当前放入位置 */ 32 | iget = 0; /* 缓冲区的当前取出位置 */ 33 | n = 0; /* 环形缓冲区中的元素总数量 */ 34 | } 35 | /************************************************************************************************** 36 | ** 37 | ** 38 | ** 39 | **************************************************************************************************/ 40 | void ringreset() { 41 | iput = 0; /* 环形缓冲区的当前放入位置 */ 42 | iget = 0; /* 缓冲区的当前取出位置 */ 43 | n = 0; /* 环形缓冲区中的元素总数量 */ 44 | } 45 | /************************************************************************************************** 46 | ** 47 | ** 48 | ** 49 | **************************************************************************************************/ 50 | void ringfree(void) { 51 | int i; 52 | printf("begin free mem\n"); 53 | for (i = 0; i < NMAX; i++) { 54 | free(ringfifo[i].buffer); 55 | ringfifo[i].size = 0; 56 | } 57 | } 58 | /************************************************************************************************** 59 | ** 60 | ** 61 | ** 62 | **************************************************************************************************/ 63 | int addring(int i) { return (i + 1) == NMAX ? 0 : i + 1; } 64 | 65 | /************************************************************************************************** 66 | ** 67 | ** 68 | ** 69 | **************************************************************************************************/ 70 | /* 从环形缓冲区中取一个元素 */ 71 | 72 | int ringget(struct ringbuf *getinfo) { 73 | int Pos; 74 | if (n > 0) { 75 | Pos = iget; 76 | iget = addring(iget); 77 | n--; 78 | getinfo->buffer = (ringfifo[Pos].buffer); 79 | getinfo->frame_type = ringfifo[Pos].frame_type; 80 | getinfo->size = ringfifo[Pos].size; 81 | return ringfifo[Pos].size; 82 | } else { 83 | return 0; 84 | } 85 | } 86 | /************************************************************************************************** 87 | ** 88 | ** 89 | ** 90 | **************************************************************************************************/ 91 | /* 向环形缓冲区中放入一个元素*/ 92 | void ringput(unsigned char *buffer, int size, int encode_type) { 93 | 94 | if (n < NMAX) { 95 | memcpy(ringfifo[iput].buffer, buffer, size); 96 | ringfifo[iput].size = size; 97 | ringfifo[iput].frame_type = encode_type; 98 | iput = addring(iput); 99 | n++; 100 | } 101 | } 102 | 103 | /************************************************************************************************** 104 | **将H264流数据放到ringfifo[iput].buffer里以便schedule_do线程从ringfifo[iput].buffer里取出数据发送出去 105 | **同是在DESCRIBE步骤中会对SPS,PPS编码发送给客户端,后面好像就只编码但没有发送出去 106 | ** 107 | **************************************************************************************************/ 108 | HI_S32 HisiPutH264DataToBuffer(VENC_STREAM_S *pstStream) { 109 | HI_S32 i, j; 110 | HI_S32 len = 0, off = 0, len2 = 2; 111 | unsigned char *pstr; 112 | int iframe = 0; 113 | for (i = 0; i < pstStream->u32PackCount; i++) { 114 | len += pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset; 115 | } 116 | 117 | int testlen = 0; 118 | if (n < NMAX) { 119 | for (i = 0; i < pstStream->u32PackCount; i++) { 120 | 121 | memcpy( 122 | ringfifo[iput].buffer + off, 123 | pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset, 124 | pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset); 125 | pstr = pstStream->pstPack[i].pu8Addr + 126 | pstStream->pstPack[i] 127 | .u32Offset; //计算当前PACK的有效数据的首地址 128 | off += pstStream->pstPack[i].u32Len - 129 | pstStream->pstPack[i] 130 | .u32Offset; //计算下个PACK存放到ring里的首地址 131 | 132 | if (pstr[4] == 133 | 0x67) { //使用 base64 对 data 134 | //进行编码,设计此种编码是为了使二进制数据可以通过 135 | //非纯 8-bit 的传输层传输,例如电子邮件的主体 136 | //在网络上基本上是非纯8位传输,所以要将数据在服务器编码为 137 | // base64(非8位的,6位的),然后由客户端解码 138 | //将H264数据用base64编码然后才发送 139 | UpdateSps(ringfifo[iput].buffer + off, 9); 140 | iframe = 1; 141 | } 142 | if (pstr[4] == 0x68) { 143 | UpdatePps(ringfifo[iput].buffer + off, 4); 144 | } 145 | } 146 | 147 | ringfifo[iput].size = len; 148 | if (iframe) { 149 | ringfifo[iput].frame_type = FRAME_TYPE_I; 150 | } 151 | 152 | else 153 | ringfifo[iput].frame_type = FRAME_TYPE_P; 154 | iput = addring(iput); 155 | n++; 156 | } 157 | 158 | return HI_SUCCESS; 159 | } 160 | -------------------------------------------------------------------------------- /src/rtsp/ringfifo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct ringbuf { 5 | unsigned char *buffer; 6 | int frame_type; 7 | int size; 8 | }; 9 | int addring(int i); 10 | int ringget(struct ringbuf *getinfo); 11 | void ringput(unsigned char *buffer, int size, int encode_type); 12 | void ringfree(); 13 | void ringmalloc(int size); 14 | void ringreset(); 15 | 16 | HI_S32 HisiPutH264DataToBuffer(VENC_STREAM_S *pstStream); 17 | -------------------------------------------------------------------------------- /src/rtsp/rtputils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "rtputils.h" 16 | #include "rtspservice.h" 17 | #include "rtsputils.h" 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | typedef struct { 23 | /**/ /* byte 0 */ 24 | unsigned char u4CSrcLen : 4; /**/ /* expect 0 */ 25 | unsigned char u1Externsion : 1; /**/ /* expect 1, see RTP_OP below */ 26 | unsigned char u1Padding : 1; /**/ /* expect 0 */ 27 | unsigned char u2Version : 2; /**/ /* expect 2 */ 28 | /**/ /* byte 1 */ 29 | unsigned char u7Payload : 7; /**/ /* RTP_PAYLOAD_RTSP */ 30 | unsigned char u1Marker : 1; /**/ /* expect 1 */ 31 | /**/ /* bytes 2, 3 */ 32 | unsigned short u16SeqNum; 33 | /**/ /* bytes 4-7 */ 34 | unsigned long long u32TimeStamp; 35 | /**/ /* bytes 8-11 */ 36 | unsigned long u32SSrc; /**/ /* stream number is used here. */ 37 | } StRtpFixedHdr; 38 | 39 | typedef struct { 40 | // byte 0 41 | unsigned char u5Type : 5; 42 | unsigned char u2Nri : 2; 43 | unsigned char u1F : 1; 44 | } StNaluHdr; /**/ /* 1 BYTES */ 45 | 46 | typedef struct { 47 | // byte 0 48 | unsigned char u5Type : 5; 49 | unsigned char u2Nri : 2; 50 | unsigned char u1F : 1; 51 | } StFuIndicator; /**/ /* 1 BYTES */ 52 | 53 | typedef struct { 54 | // byte 0 55 | unsigned char u5Type : 5; 56 | unsigned char u1R : 1; 57 | unsigned char u1E : 1; 58 | unsigned char u1S : 1; 59 | } StFuHdr; /**/ /* 1 BYTES */ 60 | 61 | typedef struct _tagStRtpHandle { 62 | int s32Sock; 63 | struct sockaddr_in stServAddr; 64 | unsigned short u16SeqNum; 65 | unsigned long long u32TimeStampInc; 66 | unsigned long long u32TimeStampCurr; 67 | unsigned long long u32CurrTime; 68 | unsigned long long u32PrevTime; 69 | unsigned int u32SSrc; 70 | StRtpFixedHdr *pRtpFixedHdr; 71 | StNaluHdr *pNaluHdr; 72 | StFuIndicator *pFuInd; 73 | StFuHdr *pFuHdr; 74 | EmRtpPayload emPayload; 75 | #ifdef SAVE_NALU 76 | FILE *pNaluFile; 77 | #endif 78 | } StRtpObj, *HndRtp; 79 | /************************************************************************************************** 80 | **返回hndRtp结构体的首地址,是个数字。然后外部使用时又将这个数字强制转为hndRtp结构体指针 81 | **功能:创建RTP套接字(里面设置IP,端口,负荷,时间) 82 | **参数: 83 | u32IP: IN,目的端IP 84 | s32Port: IN,目的端端口 85 | emPayload:IN,负荷类型 86 | 返回:成功返回HndRtp句柄,失败返回0 87 | **************************************************************************************************/ 88 | unsigned int 89 | RtpCreate(unsigned int u32IP, int s32Port, EmRtpPayload emPayload) { 90 | HndRtp hRtp = NULL; 91 | struct timeval stTimeval; 92 | struct ifreq stIfr; 93 | int s32Broadcast = 1; 94 | 95 | hRtp = (HndRtp)calloc(1, sizeof(StRtpObj)); 96 | if (NULL == hRtp) { 97 | printf("Failed to create RTP handle\n"); 98 | goto cleanup; 99 | } 100 | 101 | hRtp->s32Sock = -1; 102 | if ((hRtp->s32Sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 103 | printf("Failed to create socket\n"); 104 | goto cleanup; 105 | } 106 | 107 | if (0xFF000000 == (u32IP & 0xFF000000)) { 108 | if (-1 == setsockopt( 109 | hRtp->s32Sock, SOL_SOCKET, SO_BROADCAST, 110 | (char *)&s32Broadcast, sizeof(s32Broadcast))) { 111 | printf("Failed to set socket\n"); 112 | goto cleanup; 113 | } 114 | } 115 | 116 | hRtp->stServAddr.sin_family = AF_INET; 117 | hRtp->stServAddr.sin_port = htons(s32Port); 118 | hRtp->stServAddr.sin_addr.s_addr = u32IP; 119 | bzero(&(hRtp->stServAddr.sin_zero), 8); 120 | 121 | //初始化序号 122 | hRtp->u16SeqNum = 0; 123 | //初始化时间戳 124 | hRtp->u32TimeStampInc = 0; 125 | hRtp->u32TimeStampCurr = 0; 126 | 127 | //获取当前时间 128 | if (gettimeofday(&stTimeval, NULL) == -1) { 129 | printf("Failed to get os time\n"); 130 | goto cleanup; 131 | } 132 | 133 | hRtp->u32PrevTime = stTimeval.tv_sec * 1000 + stTimeval.tv_usec / 1000; 134 | 135 | hRtp->emPayload = emPayload; 136 | 137 | //获取本机网络设备名 138 | strcpy(stIfr.ifr_name, "eth0"); 139 | if (ioctl(hRtp->s32Sock, SIOCGIFADDR, &stIfr) < 0) { 140 | // printf("Failed to get host ip\n"); 141 | strcpy(stIfr.ifr_name, "wlan0"); 142 | if (ioctl(hRtp->s32Sock, SIOCGIFADDR, &stIfr) < 0) { 143 | printf("Failed to get host eth0 or wlan0 ip\n"); 144 | goto cleanup; 145 | } 146 | } 147 | 148 | hRtp->u32SSrc = 149 | htonl(((struct sockaddr_in *)(&stIfr.ifr_addr))->sin_addr.s_addr); 150 | 151 | printf("<><><><>success creat RTP<><><><>\n"); 152 | 153 | return (unsigned int)hRtp; 154 | 155 | cleanup: 156 | if (hRtp) { 157 | if (hRtp->s32Sock >= 0) { 158 | close(hRtp->s32Sock); 159 | } 160 | 161 | free(hRtp); 162 | } 163 | 164 | return 0; 165 | } 166 | /************************************************************************************************** 167 | **取消视频发送:只要找到RTP的会话,关闭其套接字,然后释放HndRtp即可 168 | ** 169 | ** 170 | **************************************************************************************************/ 171 | void RtpDelete(unsigned int u32Rtp) { 172 | HndRtp hRtp = (HndRtp)u32Rtp; 173 | 174 | if (hRtp) { 175 | if (hRtp->s32Sock >= 0) { 176 | close(hRtp->s32Sock); 177 | } 178 | 179 | free(hRtp); 180 | } 181 | } 182 | /************************************************************************************************** 183 | **将H264NALU变成RTP,需要加一些东西 184 | **参数: 185 | hRtp: in/out需要填写的RTP结构体 186 | pNalBuf: in H264NALU的内容 187 | s32NalBufSize:in H264NALU的大小 188 | ** 189 | //注意这里的pNalBuf包含了00 00 00 01这几个首字节 190 | 抓包第0x2a-0x35=12个字节表示RTP头 191 | 第0x36为FA-U指示器(需要H264第一个00参与计算) 192 | 第0x37为FA-U头(需要H264第一个00参与计算) 193 | 第0x38以后为真正的H264数据,如果是第一个FA-U包的话的,因为H264的第一个00未编进来, 194 | 所以抓包看H264开头为00 00 01,少了一个00 195 | 发送的缓冲区pSendBuf第0-11字节为RTP头 196 | 第12字节为FA-U指示器 197 | 第13字节为FA-U头部 198 | 第14字节开始与H264数据的第1个字节一一对应(第0字节没有存进来) 199 | 查资料感觉FA-U头低5位表示这个FA-U包的类型,应该与真正的H264的NALU的类型一致,即应该是 200 | 00 00 00 01 67的第4个字节67参与计算 201 | **************************************************************************************************/ 202 | static int SendNalu264(HndRtp hRtp, char *pNalBuf, int s32NalBufSize) { 203 | char *pNaluPayload; 204 | char *pSendBuf; 205 | int s32Bytes = 0; 206 | int s32Ret = 0; 207 | struct timeval stTimeval; 208 | char *pNaluCurr; 209 | int s32NaluRemain; 210 | unsigned char u8NaluBytes; 211 | 212 | pSendBuf = (char *)calloc(MAX_RTP_PKT_LENGTH + 100, sizeof(char)); 213 | if (NULL == pSendBuf) { 214 | s32Ret = -1; 215 | goto cleanup; 216 | } 217 | //由H264NALU变成RTP,需要增加一些东西,具体内容网上查RTP协议 218 | // pRtpFixedHdr:RTP固定头部fix固定,Hdr:header头部 219 | // pNaluHdr:Nalu包头部 220 | // pFuInd:FU-A indicator FU-A 包指示器 221 | // pFuHdr:FU-A header FU-A 包头部 222 | // FU-A=FU-A indicator+FU-A header +分片单元荷载 223 | /*pSendBuf指向一块大小1500字节的缓冲区 224 | hRtp->pRtpFixedHdr = (StRtpFixedHdr 225 | *)pSendBuf;表明hRtp->pRtpFixedHdr指向了pSendBuf的前面12字节 单个NAL包: 226 | hRtp->pNaluHdr = (StNaluHdr *)(pSendBuf + 227 | 12);表明hRtp->pNaluHdr 指向了pSendBuf的第12字节(从0开始计算) pNaluPayload 228 | = (pSendBuf + 13);表明真正的H264数据存在pSendBuf的第13字节处 FU-A包: 229 | hRtp->pFuInd = (StFuIndicator *)(pSendBuf + 12);表明fu 230 | indicator指向了pSendBuf的第12字节 hRtp->pFuHdr = (StFuHdr 231 | *)(pSendBuf + 13);表明fu header位置指向了pSendBuf的第13字节 pNaluPayload 232 | = (pSendBuf + 14);表明真正的H264数据存在pSendBuf的第14字节处 233 | */ 234 | 235 | hRtp->pRtpFixedHdr = (StRtpFixedHdr *)pSendBuf; 236 | hRtp->pRtpFixedHdr->u2Version = 2; // RTP协议的版本号 237 | 238 | hRtp->pRtpFixedHdr->u1Marker = 239 | 0; //对视频来说表示标记一帧的结束,后面会被改写为1 240 | hRtp->pRtpFixedHdr->u7Payload = H264; //有效荷载类型,简单点用96表示无类型 241 | 242 | //计算时间戳 243 | hRtp->pRtpFixedHdr->u32TimeStamp = 244 | htonl(hRtp->u32TimeStampCurr * (90000 / 1000)); //时间戳 245 | 246 | hRtp->pRtpFixedHdr->u32SSrc = 247 | hRtp->u32SSrc; //同步信源(SSRC)标识符,一般填客户机的IP 248 | 249 | if (gettimeofday(&stTimeval, NULL) == -1) { 250 | printf("Failed to get os time\n"); 251 | s32Ret = -1; 252 | goto cleanup; 253 | } 254 | 255 | //保存nalu首byte 256 | u8NaluBytes = *pNalBuf; //注意这里的pNalBuf包含了00 00 00 257 | // 01这几个首字节,所以u8NaluBytes=00 258 | //设置未发送的Nalu数据指针位置 259 | pNaluCurr = pNalBuf + 1; 260 | /*这里的pNaluCurr是真正的H264数据的第二个,第一个被赋值给了u8NaluBytes参与FU-A 261 | indicator与FU-A header 的计算 如果原来的H264数据为:00 00 00 01 xx xx 262 | xx...... 那么u8NaluBytes为第一个00 pNaluCurr则为00 00 01 xx xx xx...... 263 | 所以后面 memcpy(pNaluPayload, pNaluCurr, s32Bytes)时pNaluPayload也为00 00 01 264 | 67 xx xx xx......这样发送出去后抓包发现 FA-U的第一个包为00 00 01 xx xx 265 | xx...... 266 | */ 267 | //设置剩余的Nalu数据数量 268 | s32NaluRemain = s32NalBufSize - 1; 269 | 270 | // NALU包小于等于最大包长度,直接发送 271 | if (s32NaluRemain <= MAX_RTP_PKT_LENGTH) { 272 | hRtp->pRtpFixedHdr->u1Marker = 273 | 1; //对视频来说表示标记一帧的结束,这里一只一个NALU包,一包即结束,所以为1 274 | hRtp->pRtpFixedHdr->u16SeqNum = 275 | htons(hRtp->u16SeqNum++); //标识发送者所发送的RTP报文的序列号 276 | //用网络字节表示 277 | hRtp->pNaluHdr = (StNaluHdr *)(pSendBuf + 12); 278 | hRtp->pNaluHdr->u1F = (u8NaluBytes & 0x80) >> 7; 279 | hRtp->pNaluHdr->u2Nri = (u8NaluBytes & 0x60) >> 5; 280 | hRtp->pNaluHdr->u5Type = u8NaluBytes & 0x1f; 281 | 282 | pNaluPayload = (pSendBuf + 13); 283 | memcpy(pNaluPayload, pNaluCurr, s32NaluRemain); 284 | 285 | s32Bytes = s32NaluRemain + 13; 286 | /*int sendto ( socket s , const void * msg, int len, unsigned int flags, 287 | const struct sockaddr * to , int tolen ) ; s 套接字 msg 288 | 待发送数据的缓冲区 len 缓冲区长度 flags 调用方式标志位, 一般为0, 289 | 改变Flags,将会改变Sendto发送的形式 to 290 | (可选)指针,指向目的套接字的地址 tolen 所指目的地址的长度 291 | 返回:成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 292 | 中 293 | */ 294 | if (sendto( 295 | hRtp->s32Sock, pSendBuf, s32Bytes, 0, 296 | (struct sockaddr *)&hRtp->stServAddr, 297 | sizeof(hRtp->stServAddr)) < 0) { 298 | s32Ret = -1; 299 | goto cleanup; 300 | } 301 | #ifdef SAVE_NALU 302 | fwrite(pSendBuf, s32Bytes, 1, hRtp->pNaluFile); 303 | #endif 304 | } 305 | // NALU包大于最大包长度,分批发送 FA-U模式 306 | else { 307 | //指定fu indicator位置 308 | hRtp->pFuInd = (StFuIndicator *)(pSendBuf + 12); 309 | hRtp->pFuInd->u1F = (u8NaluBytes & 0x80) >> 7; 310 | hRtp->pFuInd->u2Nri = (u8NaluBytes & 0x60) >> 5; 311 | hRtp->pFuInd->u5Type = 312 | 28; //前面说明了u8NaluBytes=00,计算hRtp->pFuInd=28= 313 | // 0x1c,抓包发现第一个会有0x1c(抓包的第0x36个) 314 | 315 | //指定fu header位置 316 | hRtp->pFuHdr = (StFuHdr *)(pSendBuf + 13); 317 | hRtp->pFuHdr->u1R = 0; //协议规定必须为0 318 | hRtp->pFuHdr->u5Type = 319 | u8NaluBytes & 320 | 0x1f; //前面说明了u8NaluBytes=00,所以hRtp->pFuHdr->u5Type =0 321 | 322 | //指定payload位置 323 | pNaluPayload = (pSendBuf + 14); 324 | 325 | //当剩余Nalu数据多于0时分批发送nalu数据 326 | while (s32NaluRemain > 0) { 327 | /*配置fixed header*/ 328 | //每个包序号增1 329 | hRtp->pRtpFixedHdr->u16SeqNum = htons(hRtp->u16SeqNum++); 330 | hRtp->pRtpFixedHdr->u1Marker = (s32NaluRemain <= MAX_RTP_PKT_LENGTH) 331 | ? 1 332 | : 0; //最后一个包u1Marker才置1 333 | 334 | /*配置fu header*/ 335 | //最后一批数据则置1 336 | hRtp->pFuHdr->u1E = 337 | (s32NaluRemain <= MAX_RTP_PKT_LENGTH) 338 | ? 1 339 | : 0; // u1E:end,代表最后一批,不是最后一批数据则为0 340 | //第一批数据则置1 341 | hRtp->pFuHdr->u1S = 342 | (s32NaluRemain == (s32NalBufSize - 1)) 343 | ? 1 344 | : 0; // u1S:start,代表第一批,第一批数据则为1 345 | /* 346 | hRtp->pFuHdr->u5Type = u8NaluBytes & 347 | 0x1f;//前面说明了u8NaluBytes=00,所以hRtp->pFuHdr->u5Type=0 348 | hRtp->pFuHdr->u1R = 0; 349 | hRtp->pFuHdr->u1E = (s32NaluRemain <= MAX_RTP_PKT_LENGTH) ? 1 : 350 | 0;//不是最后一批数据则为0 hRtp->pFuHdr->u1S = (s32NaluRemain == 351 | (s32NalBufSize - 1)) ? 1 : 0;//第一批数据则为1 352 | 这样计算起来FA-U包第一个包为10000000=0x80,所以抓包看到0x80(抓包的第0x37个) 353 | 第二个包为00000000=0x00, 354 | 所以抓包看到0x00(抓包的第0x37个) 355 | 最后一个包为01000000=0x40,所以抓包看到0x00(抓包的第0x37个) 356 | */ 357 | 358 | s32Bytes = (s32NaluRemain < MAX_RTP_PKT_LENGTH) 359 | ? s32NaluRemain 360 | : MAX_RTP_PKT_LENGTH; 361 | 362 | memcpy(pNaluPayload, pNaluCurr, s32Bytes); 363 | 364 | //发送本批次 365 | s32Bytes = s32Bytes + 14; 366 | if (sendto( 367 | hRtp->s32Sock, pSendBuf, s32Bytes, 0, 368 | (struct sockaddr *)&hRtp->stServAddr, 369 | sizeof(hRtp->stServAddr)) < 0) { 370 | s32Ret = -1; 371 | goto cleanup; 372 | } 373 | #ifdef SAVE_NALU 374 | fwrite(pSendBuf, s32Bytes, 1, hRtp->pNaluFile); 375 | #endif 376 | 377 | //指向下批数据 378 | pNaluCurr += MAX_RTP_PKT_LENGTH; 379 | //计算剩余的nalu数据长度 380 | s32NaluRemain -= MAX_RTP_PKT_LENGTH; 381 | } 382 | } 383 | 384 | cleanup: 385 | if (pSendBuf) { 386 | free((void *)pSendBuf); 387 | } 388 | 389 | return s32Ret; 390 | } 391 | /************************************************************************************************** 392 | ** 393 | ** 394 | ** 395 | **************************************************************************************************/ 396 | static int SendNalu711(HndRtp hRtp, char *buf, int bufsize) { 397 | char *pSendBuf; 398 | int s32Bytes = 0; 399 | int s32Ret = 0; 400 | 401 | pSendBuf = (char *)calloc(MAX_RTP_PKT_LENGTH + 100, sizeof(char)); 402 | if (NULL == pSendBuf) { 403 | s32Ret = -1; 404 | goto cleanup; 405 | } 406 | hRtp->pRtpFixedHdr = (StRtpFixedHdr *)pSendBuf; 407 | hRtp->pRtpFixedHdr->u7Payload = G711; 408 | hRtp->pRtpFixedHdr->u2Version = 2; 409 | 410 | hRtp->pRtpFixedHdr->u1Marker = 1; //标志位,由具体协议规定其值。 411 | 412 | hRtp->pRtpFixedHdr->u32SSrc = hRtp->u32SSrc; 413 | 414 | hRtp->pRtpFixedHdr->u16SeqNum = htons(hRtp->u16SeqNum++); 415 | 416 | memcpy(pSendBuf + 12, buf, bufsize); 417 | 418 | hRtp->pRtpFixedHdr->u32TimeStamp = htonl(hRtp->u32TimeStampCurr); 419 | // printf("SendNalu711 timestamp:%lld\n",hRtp->pRtpFixedHdr->u32TimeStamp); 420 | s32Bytes = bufsize + 12; 421 | if (sendto( 422 | hRtp->s32Sock, pSendBuf, s32Bytes, 0, 423 | (struct sockaddr *)&hRtp->stServAddr, 424 | sizeof(hRtp->stServAddr)) < 0) { 425 | printf("Failed to send!"); 426 | s32Ret = -1; 427 | goto cleanup; 428 | } 429 | cleanup: 430 | if (pSendBuf) { 431 | free((void *)pSendBuf); 432 | } 433 | return s32Ret; 434 | } 435 | /************************************************************************************************** 436 | ** 437 | ** 438 | ** 439 | **************************************************************************************************/ 440 | unsigned int RtpSend( 441 | unsigned int u32Rtp, char *pData, int s32DataSize, 442 | unsigned int u32TimeStamp) { 443 | int s32NalSize = 0; 444 | char *pNalBuf, *pDataEnd; 445 | HndRtp hRtp = (HndRtp)u32Rtp; 446 | unsigned int u32NaluToken; 447 | 448 | hRtp->u32TimeStampCurr = u32TimeStamp; 449 | 450 | if (_h264 == 451 | hRtp->emPayload) //发送H264文件,有多个NALU单元,需要找出00000001来分离NALU 452 | { // printf("\n\t\th264"); 453 | pDataEnd = pData + s32DataSize; 454 | //搜寻第一个nalu起始标志0x01000000 455 | for (; pData < pDataEnd - 5; pData++) { 456 | memcpy(&u32NaluToken, pData, 4 * sizeof(char)); 457 | if (0x01000000 == u32NaluToken) { 458 | //标记nalu起始位置 459 | pData += 4; 460 | pNalBuf = pData; 461 | break; 462 | } 463 | } 464 | //发送nalu 465 | for (; pData < pDataEnd - 5; pData++) { 466 | //搜寻第二个nalu起始标志0x01000000,找到nalu起始位置,发送该nalu数据 467 | // 468 | memcpy(&u32NaluToken, pData, 4 * sizeof(char)); 469 | if (0x01000000 == u32NaluToken) { 470 | s32NalSize = 471 | (int)(pData - pNalBuf); //二者一相减就是第一个nalu的内容 472 | if (SendNalu264(hRtp, pNalBuf, s32NalSize) == -1) { 473 | return -1; 474 | } 475 | 476 | //标记nalu起始位置 477 | pData += 4; 478 | pNalBuf = pData; 479 | } 480 | } // while 481 | //最后一个nalu 482 | if (pData > pNalBuf) { 483 | s32NalSize = (int)(pData - pNalBuf); 484 | if (SendNalu264(hRtp, pNalBuf, s32NalSize) == -1) { 485 | return -1; 486 | } 487 | } 488 | } else if ( 489 | _h264nalu == hRtp->emPayload) //直接发送NALU单元,所以不需要分享NALU 490 | { //在rtsp的setup阶段时创建RTP套接字时设置了负荷类型为_h264nalu,所以程序执行这个分支 491 | //原因3518编码已经帮我们分开了每一个H264的slice,即直接是一个NALU,所以直接添加东西组成RTP即可 492 | // printf("\n\t\th264nalu");经打印也验证是执行这个分支,即是_h264nalu 493 | if (SendNalu264(hRtp, pData, s32DataSize) == -1) { 494 | return -1; 495 | } 496 | } else if (_g711 == hRtp->emPayload) { 497 | printf("\n\t\tg711"); 498 | if (SendNalu711(hRtp, pData, s32DataSize) == -1) { 499 | return -1; 500 | } 501 | } else { 502 | return -1; 503 | } 504 | 505 | return 0; 506 | } 507 | /************************************************************************************************** 508 | ** 509 | ** 510 | ** 511 | **************************************************************************************************/ 512 | 513 | #ifdef __cplusplus 514 | } 515 | #endif 516 | -------------------------------------------------------------------------------- /src/rtsp/rtputils.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTPUTILS_H 2 | #define _RTPUTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #define MAX_RTP_PKT_LENGTH 1400 15 | 16 | #define H264 96 17 | #define G711 97 18 | 19 | typedef enum { 20 | _h264 = 0x100, 21 | _h264nalu, 22 | _mjpeg, 23 | _g711 = 0x200, 24 | } EmRtpPayload; 25 | enum H264_FRAME_TYPE { FRAME_TYPE_I, FRAME_TYPE_P, FRAME_TYPE_B }; 26 | 27 | unsigned int RtpCreate(unsigned int u32IP, int s32Port, EmRtpPayload emPayload); 28 | void RtpDelete(unsigned int u32Rtp); 29 | unsigned int RtpSend( 30 | unsigned int u32Rtp, char *pData, int s32DataSize, 31 | unsigned int u32TimeStamp); 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | #endif /* _RTPUTILS_H */ 38 | -------------------------------------------------------------------------------- /src/rtsp/rtspservice.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTSP_H 2 | #define _RTSP_H 3 | #include "rtsputils.h" 4 | 5 | #define RTSP_DEBUG 1 6 | #define RTP_DEFAULT_PORT 5004 7 | 8 | void PrefsInit(); 9 | void RTP_port_pool_init(int port); 10 | void EventLoop(int s32MainFd); 11 | 12 | void CallBackNotifyRtspExit(char s8IsExit); 13 | void *ThreadRtsp(void *pArgs); 14 | int rtsp_server(RTSP_buffer *rtsp); 15 | void IntHandl(int i); 16 | void UpdateSps(unsigned char *data, int len); 17 | void UpdatePps(unsigned char *data, int len); 18 | 19 | #endif /* _RTSP_H */ 20 | -------------------------------------------------------------------------------- /src/rtsp/rtsputils.c: -------------------------------------------------------------------------------- 1 | #include "fcntl.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "ringfifo.h" 14 | #include "rtputils.h" 15 | #include "rtspservice.h" 16 | #include "rtsputils.h" 17 | extern int g_s32DoPlay; 18 | //===============add=================== 19 | 20 | //===================================== 21 | /* 22 | ip地址 的二进制数值 转化为用于网络传输的字符串 23 | */ 24 | char *sock_ntop_host( 25 | const struct sockaddr *sa, socklen_t salen, char *str, size_t len) { 26 | switch (sa->sa_family) { 27 | case AF_INET: { 28 | struct sockaddr_in *sin = (struct sockaddr_in *)sa; 29 | 30 | if (inet_ntop(AF_INET, &sin->sin_addr, str, len) == NULL) 31 | return (NULL); 32 | return (str); 33 | } 34 | 35 | default: 36 | snprintf( 37 | str, len, "sock_ntop_host: unknown AF_xxx: %d, len %d", 38 | sa->sa_family, salen); 39 | return (str); 40 | } 41 | return (NULL); 42 | } 43 | /*接收连接,创建一个新的socket,返回其描述符*/ 44 | //无阻塞查询是否有连接,有连接时f>0,无连接时f<0 45 | int tcp_accept(int fd) { 46 | int f; 47 | struct sockaddr_storage addr; 48 | socklen_t addrlen = sizeof(addr); 49 | 50 | memset(&addr, 0, sizeof(addr)); 51 | addrlen = sizeof(addr); 52 | 53 | /*接收连接,创建一个新的socket,返回其描述符*/ 54 | //无阻塞查询是否有连接,有连接时返回值>0,无连接时返回值<0 55 | f = accept(fd, (struct sockaddr *)&addr, &addrlen); 56 | 57 | return f; 58 | } 59 | 60 | void tcp_close(int s) { close(s); } 61 | 62 | int tcp_connect(unsigned short port, char *addr) { 63 | int f; 64 | int on = 1; 65 | int one = 1; /*used to set SO_KEEPALIVE*/ 66 | 67 | struct sockaddr_in s; 68 | int v = 1; 69 | if ((f = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { 70 | fprintf(stderr, "socket() error in tcp_connect.\n"); 71 | return -1; 72 | } 73 | setsockopt(f, SOL_SOCKET, SO_REUSEADDR, (char *)&v, sizeof(int)); 74 | s.sin_family = AF_INET; 75 | s.sin_addr.s_addr = inet_addr(addr); // htonl(addr); 76 | s.sin_port = htons(port); 77 | // set to non-blocking 78 | if (ioctl(f, FIONBIO, &on) < 0) { 79 | fprintf(stderr, "ioctl() error in tcp_connect.\n"); 80 | return -1; 81 | } 82 | if (connect(f, (struct sockaddr *)&s, sizeof(s)) < 0) { 83 | fprintf(stderr, "connect() error in tcp_connect.\n"); 84 | return -1; 85 | } 86 | if (setsockopt(f, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0) { 87 | fprintf(stderr, "setsockopt() SO_KEEPALIVE error in tcp_connect.\n"); 88 | return -1; 89 | } 90 | return f; 91 | } 92 | /*创建、设置、绑定、监听套接字,并将套接字设为非阻塞模式 93 | 参数:套接字的端口 94 | */ 95 | int tcp_listen(unsigned short port) { 96 | int f; 97 | int on = 1; 98 | 99 | struct sockaddr_in s; 100 | int v = 1; 101 | 102 | /*创建套接字*/ 103 | if ((f = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 104 | fprintf(stderr, "socket() error in tcp_listen.\n"); 105 | return -1; 106 | } 107 | 108 | /*设置socket的可选参数*/ 109 | setsockopt(f, SOL_SOCKET, SO_REUSEADDR, (char *)&v, sizeof(int)); 110 | 111 | s.sin_family = AF_INET; 112 | s.sin_addr.s_addr = htonl(INADDR_ANY); 113 | s.sin_port = htons(port); 114 | 115 | /*绑定socket*/ 116 | if (bind(f, (struct sockaddr *)&s, sizeof(s))) { 117 | fprintf(stderr, "bind() error in tcp_listen"); 118 | return -1; 119 | } 120 | 121 | //设置为非阻塞方式 122 | if (ioctl(f, FIONBIO, &on) < 0) { 123 | fprintf(stderr, "ioctl() error in tcp_listen.\n"); 124 | return -1; 125 | } 126 | 127 | /*监听*/ 128 | if (listen(f, SOMAXCONN) < 0) { 129 | fprintf(stderr, "listen() error in tcp_listen.\n"); 130 | return -1; 131 | } 132 | 133 | return f; 134 | } 135 | //读取客户端RTSP包的IP,PORT及判断RTSP包的大小和内容 136 | /* 137 | *读取客户端RTSP包的IP,PORT 138 | *fd: 接收端套接字描述符; 139 | *buffer: 输出参数: 用来存放recv函数接收到的数据 140 | *nbytes: 指明buf的长度; 141 | *Addr: 输出,用来保存客户端IP 142 | *一般置为0。 143 | *返回值:<0 出错 =0 连接关闭 >0 接收到数据大小, 144 | */ 145 | int tcp_read(int fd, void *buffer, int nbytes, struct sockaddr *Addr) { 146 | int n; 147 | socklen_t Addrlen = sizeof(struct sockaddr); 148 | char addr_str[128]; 149 | n = recv(fd, buffer, nbytes, 0); 150 | if (n > 0) { 151 | //获取对方IP信息,Addr是输出参数,保存函数执行后的结果 152 | if (getpeername(fd, Addr, &Addrlen) < 0) { 153 | fprintf(stderr, "error getperrname:%s %i\n", __FILE__, __LINE__); 154 | } else { 155 | //打印出IP和port 156 | fprintf( 157 | stderr, "%s ", 158 | sock_ntop_host(Addr, Addrlen, addr_str, sizeof(addr_str))); 159 | fprintf( 160 | stderr, "Port:%d\n", 161 | ntohs(((struct sockaddr_in *)Addr)->sin_port)); 162 | } 163 | } 164 | 165 | return n; 166 | } 167 | 168 | /* 169 | int tcp_write(int fd, void *buffer, int nbytes) 170 | { 171 | int n; 172 | n = write(fd, buffer, nbytes); 173 | 174 | return n; 175 | } 176 | */ 177 | 178 | int tcp_write(int connectSocketId, char *dataBuf, int dataSize) { 179 | int actDataSize; 180 | 181 | //发送数据 182 | while (dataSize > 0) { 183 | actDataSize = send(connectSocketId, dataBuf, dataSize, 0); 184 | 185 | if (actDataSize <= 0) 186 | break; 187 | 188 | dataBuf += actDataSize; 189 | dataSize -= actDataSize; 190 | } 191 | 192 | if (dataSize > 0) { 193 | printf("Send Data error\n"); 194 | return -1; 195 | } 196 | 197 | return 0; 198 | } 199 | 200 | /* schedule 相关 */ 201 | stScheList sched[MAX_CONNECTION]; 202 | 203 | int stop_schedule = 0; //是否退出schedule 204 | int num_conn = 2; /*连接个数*/ 205 | 206 | /* 207 | 初始化调度任务列表,并创建schedule_do线程 208 | */ 209 | int ScheduleInit() { 210 | int i; 211 | pthread_t thread = 0; 212 | 213 | /*初始化数据*/ 214 | for (i = 0; i < MAX_CONNECTION; ++i) { 215 | sched[i].rtp_session = NULL; 216 | sched[i].play_action = NULL; 217 | sched[i].valid = 0; 218 | sched[i].BeginFrame = 0; 219 | } 220 | 221 | /*创建处理主线程*/ 222 | pthread_create(&thread, NULL, schedule_do, NULL); 223 | 224 | return 0; 225 | } 226 | 227 | void *schedule_do(void *arg) { 228 | int i = 0; 229 | struct timeval now; 230 | unsigned long long mnow; 231 | char *pDataBuf, *pFindNal; 232 | unsigned int ringbuffer; 233 | struct timespec ts = {0, 33333}; 234 | int s32FileId; 235 | unsigned int u32NaluToken; 236 | char *pNalStart = NULL; 237 | int s32NalSize; 238 | int s32FindNal = 0; 239 | int buflen = 0, ringbuflen = 0, ringbuftype; 240 | struct ringbuf ringinfo; 241 | //===================== 242 | #ifdef RTSP_DEBUG 243 | printf("The pthread %s start\n", __FUNCTION__); 244 | #endif 245 | 246 | do { 247 | nanosleep(&ts, NULL); 248 | 249 | s32FindNal = 0; 250 | 251 | //如果有客户端连接,则g_s32DoPlay大于零 252 | ringbuflen = ringget(&ringinfo); 253 | if (ringbuflen == 0) 254 | continue; 255 | s32FindNal = 1; 256 | for (i = 0; i < MAX_CONNECTION; ++i) { 257 | if (sched[i].valid) { 258 | if (!sched[i].rtp_session->pause) { 259 | //计算时间戳 260 | gettimeofday(&now, NULL); 261 | mnow = (now.tv_sec * 1000 + now.tv_usec / 1000); //毫秒 262 | //转为hndRtp结构体指针 263 | if ((sched[i].rtp_session->hndRtp) && (s32FindNal)) { 264 | buflen = ringbuflen; 265 | if (ringinfo.frame_type == FRAME_TYPE_I) 266 | sched[i].BeginFrame = 1; 267 | sched[i].play_action( 268 | (unsigned int)(sched[i].rtp_session->hndRtp), 269 | ringinfo.buffer, ringinfo.size, mnow); 270 | } 271 | } 272 | } 273 | } 274 | //============add================ 275 | //=============================== 276 | } while (!stop_schedule); 277 | 278 | cleanup: 279 | 280 | // free(pDataBuf); 281 | // close(s32FileId); 282 | 283 | #ifdef RTSP_DEBUG 284 | printf("The pthread %s end\n", __FUNCTION__); 285 | #endif 286 | return ERR_NOERROR; 287 | } 288 | 289 | //把RTP会话添加进schedule中,并设置播放动作为发送,错误返回-1,正常返回schedule队列号 290 | //参数:in,RTP会话 291 | int schedule_add(RTP_session *rtp_session) { 292 | int i; 293 | for (i = 0; i < MAX_CONNECTION; ++i) { 294 | /*需是还没有被加入到调度队列中的会话*/ 295 | if (!sched[i].valid) { 296 | sched[i].valid = 1; 297 | sched[i].rtp_session = rtp_session; 298 | 299 | //设置播放动作 300 | sched[i].play_action = RtpSend; 301 | printf( 302 | "**adding a schedule object action %s,%d**\n", __FILE__, 303 | __LINE__); 304 | 305 | return i; 306 | } 307 | } 308 | return ERR_GENERIC; 309 | } 310 | 311 | int schedule_start(int id, stPlayArgs *args) { 312 | sched[id].rtp_session->pause = 0; 313 | sched[id].rtp_session->started = 1; 314 | 315 | //播放状态,大于零则表示有客户端播放文件 316 | g_s32DoPlay++; 317 | 318 | return ERR_NOERROR; 319 | } 320 | 321 | void schedule_stop(int id) {} 322 | 323 | int schedule_remove(int id) { 324 | sched[id].valid = 0; 325 | sched[id].BeginFrame = 0; 326 | return ERR_NOERROR; 327 | } 328 | 329 | //把需要发送的信息放入rtsp.out_buffer中 330 | int bwrite(char *buffer, unsigned short len, RTSP_buffer *rtsp) { 331 | /*检查是否有缓冲溢出*/ 332 | if ((rtsp->out_size + len) > (int)sizeof(rtsp->out_buffer)) { 333 | fprintf( 334 | stderr, "bwrite(): not enough free space in out message buffer.\n"); 335 | return ERR_ALLOC; 336 | } 337 | /*填充数据*/ 338 | memcpy(&(rtsp->out_buffer[rtsp->out_size]), buffer, len); 339 | rtsp->out_buffer[rtsp->out_size + len] = '\0'; 340 | rtsp->out_size += len; 341 | 342 | #ifdef RTSP_DEBUG 343 | printf("(Send to client:)\n%s\n", rtsp->out_buffer); 344 | #endif 345 | return ERR_NOERROR; 346 | } 347 | /* 348 | 发送响应,将错误代码err与rtsp中CSEQ写到缓冲区去 349 | err:错误代码 350 | addon:是否添加字符串,如果内容太多,这个参数为增加的内容 351 | rtsp:主要是利用里面的CSEQ 352 | */ 353 | int send_reply(int err, char *addon, RTSP_buffer *rtsp) { 354 | unsigned int len; 355 | char *b; 356 | int res; 357 | 358 | if (addon != NULL) { 359 | len = 256 + strlen(addon); 360 | } else { 361 | len = 256; 362 | } 363 | 364 | /*分配空间*/ 365 | b = (char *)malloc(len); 366 | if (b == NULL) { 367 | fprintf(stderr, "send_reply(): memory allocation error.\n"); 368 | return ERR_ALLOC; 369 | } 370 | memset(b, 0, sizeof(b)); 371 | /*按照协议格式填充数据*/ 372 | sprintf( 373 | b, "%s %d %s" RTSP_EL "CSeq: %d" RTSP_EL, RTSP_VER, err, get_stat(err), 374 | rtsp->rtsp_cseq); 375 | strcat(b, RTSP_EL); 376 | 377 | /*将数据写入到缓冲区中*/ 378 | res = bwrite(b, (unsigned short)strlen(b), rtsp); 379 | //释放空间 380 | free(b); 381 | 382 | return res; 383 | } 384 | 385 | //由错误码返回错误信息 386 | const char *get_stat(int err) { 387 | struct { 388 | const char *token; 389 | int code; 390 | } status[] = { 391 | {"Continue", 100}, 392 | {"OK", 200}, 393 | {"Created", 201}, 394 | {"Accepted", 202}, 395 | {"Non-Authoritative Information", 203}, 396 | {"No Content", 204}, 397 | {"Reset Content", 205}, 398 | {"Partial Content", 206}, 399 | {"Multiple Choices", 300}, 400 | {"Moved Permanently", 301}, 401 | {"Moved Temporarily", 302}, 402 | {"Bad Request", 400}, 403 | {"Unauthorized", 401}, 404 | {"Payment Required", 402}, 405 | {"Forbidden", 403}, 406 | {"Not Found", 404}, 407 | {"Method Not Allowed", 405}, 408 | {"Not Acceptable", 406}, 409 | {"Proxy Authentication Required", 407}, 410 | {"Request Time-out", 408}, 411 | {"Conflict", 409}, 412 | {"Gone", 410}, 413 | {"Length Required", 411}, 414 | {"Precondition Failed", 412}, 415 | {"Request Entity Too Large", 413}, 416 | {"Request-URI Too Large", 414}, 417 | {"Unsupported Media Type", 415}, 418 | {"Bad Extension", 420}, 419 | {"Invalid Parameter", 450}, 420 | {"Parameter Not Understood", 451}, 421 | {"Conference Not Found", 452}, 422 | {"Not Enough Bandwidth", 453}, 423 | {"Session Not Found", 454}, 424 | {"Method Not Valid In This State", 455}, 425 | {"Header Field Not Valid for Resource", 456}, 426 | {"Invalid Range", 457}, 427 | {"Parameter Is Read-Only", 458}, 428 | {"Unsupported transport", 461}, 429 | {"Internal Server Error", 500}, 430 | {"Not Implemented", 501}, 431 | {"Bad Gateway", 502}, 432 | {"Service Unavailable", 503}, 433 | {"Gateway Time-out", 504}, 434 | {"RTSP Version Not Supported", 505}, 435 | {"Option not supported", 551}, 436 | {"Extended Error:", 911}, 437 | {NULL, -1}}; 438 | 439 | int i; 440 | for (i = 0; status[i].code != err && status[i].code != -1; ++i) 441 | ; 442 | 443 | return status[i].token; 444 | } 445 | -------------------------------------------------------------------------------- /src/rtsp/rtsputils.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTSP_SERVICE_H 2 | #define _RTSP_SERVICE_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define trace_point() \ 11 | do { \ 12 | printf( \ 13 | "rtsp_tracepoint: %s,%s,%d\n", __FILE__, __FUNCTION__, __LINE__); \ 14 | } while (0) // 10728 15 | 16 | /*error codes define,yanf*/ 17 | #define ERR_NOERROR 0 18 | #define ERR_GENERIC -1 19 | #define ERR_NOT_FOUND -2 20 | #define ERR_PARSE -3 21 | #define ERR_ALLOC -4 22 | #define ERR_INPUT_PARAM -5 23 | #define ERR_NOT_SD -6 24 | #define ERR_UNSUPPORTED_PT -7 25 | #define ERR_EOF -8 26 | #define ERR_FATAL -9 27 | #define ERR_CONNECTION_CLOSE -10 28 | 29 | /* 消息头关键字 */ 30 | #define HDR_CONTENTLENGTH "Content-Length" 31 | #define HDR_ACCEPT "Accept" 32 | #define HDR_ALLOW "Allow" 33 | #define HDR_BLOCKSIZE "Blocksize" 34 | #define HDR_CONTENTTYPE "Content-Type" 35 | #define HDR_DATE "Date" 36 | #define HDR_REQUIRE "Require" 37 | #define HDR_TRANSPORTREQUIRE "Transport-Require" 38 | #define HDR_SEQUENCENO "SequenceNo" 39 | #define HDR_CSEQ "CSeq" 40 | #define HDR_STREAM "Stream" 41 | #define HDR_SESSION "Session" 42 | #define HDR_TRANSPORT "Transport" 43 | #define HDR_RANGE "Range" 44 | #define HDR_USER_AGENT "User-Agent" 45 | 46 | /*rtsp方法*/ 47 | #define RTSP_METHOD_MAXLEN 15 48 | #define RTSP_METHOD_DESCRIBE "DESCRIBE" 49 | #define RTSP_METHOD_ANNOUNCE "ANNOUNCE" 50 | #define RTSP_METHOD_GET_PARAMETERS "GET_PARAMETERS" 51 | #define RTSP_METHOD_OPTIONS "OPTIONS" 52 | #define RTSP_METHOD_PAUSE "PAUSE" 53 | #define RTSP_METHOD_PLAY "PLAY" 54 | #define RTSP_METHOD_RECORD "RECORD" 55 | #define RTSP_METHOD_REDIRECT "REDIRECT" 56 | #define RTSP_METHOD_SETUP "SETUP" 57 | #define RTSP_METHOD_SET_PARAMETER "SET_PARAMETER" 58 | #define RTSP_METHOD_TEARDOWN "TEARDOWN" 59 | 60 | /*rtsp方法记号ID*/ 61 | #define RTSP_ID_DESCRIBE 0 62 | #define RTSP_ID_ANNOUNCE 1 63 | #define RTSP_ID_GET_PARAMETERS 2 64 | #define RTSP_ID_OPTIONS 3 65 | #define RTSP_ID_PAUSE 4 66 | #define RTSP_ID_PLAY 5 67 | #define RTSP_ID_RECORD 6 68 | #define RTSP_ID_REDIRECT 7 69 | #define RTSP_ID_SETUP 8 70 | #define RTSP_ID_SET_PARAMETER 9 71 | #define RTSP_ID_TEARDOWN 10 72 | 73 | /* RTSP 相关 */ 74 | #define RTSP_not_full 0 75 | #define RTSP_method_rcvd 1 76 | #define RTSP_interlvd_rcvd 2 77 | 78 | #define RTSP_BUFFERSIZE 4096 79 | #define MAX_DESCR_LENGTH 4096 80 | 81 | /* Stati della macchina a stati del server rtsp*/ 82 | #define INIT_STATE 0 83 | #define READY_STATE 1 84 | #define PLAY_STATE 2 85 | 86 | #define RTSP_VER "RTSP/1.0" 87 | 88 | #define RTSP_EL "\r\n" 89 | 90 | #define PACKAGE "sunshine" 91 | #define VERSION "1.11" 92 | 93 | typedef struct { 94 | int RTP; 95 | int RTCP; 96 | } port_pair; 97 | 98 | typedef enum { RTP_no_transport = 0, RTP_rtp_avp, RTP_rtp_avp_tcp } rtp_type; 99 | 100 | typedef struct _RTP_transport { 101 | rtp_type type; 102 | int rtp_fd; 103 | union { 104 | struct { 105 | port_pair cli_ports; 106 | port_pair ser_ports; 107 | unsigned char is_multicast; 108 | } udp; 109 | struct { 110 | port_pair interleaved; 111 | } tcp; 112 | // other trasports here 113 | } u; 114 | } RTP_transport; 115 | 116 | typedef struct _RTP_session { 117 | struct _tagStRtpHandle *hndRtp; 118 | RTP_transport transport; 119 | unsigned char pause; 120 | unsigned char started; 121 | int sched_id; 122 | struct _RTP_session *next; 123 | } RTP_session; 124 | 125 | typedef struct _RTSP_session { 126 | int cur_state; /*会话状态*/ 127 | int session_id; /*会话的ID*/ 128 | 129 | RTP_session *rtp_session; /*RTP会话*/ 130 | 131 | struct _RTSP_session *next; /*下一个会话的指针,构成链表结构*/ 132 | } RTSP_session; 133 | 134 | // RTSP_buffer RTSP客户端结构体,里面包含RTSP的接收,发送数据 135 | typedef struct _RTSP_buffer { 136 | int fd; /*socket文件描述符*/ 137 | unsigned int port; /*端口号*/ 138 | 139 | struct sockaddr stClientAddr; 140 | 141 | char in_buffer[RTSP_BUFFERSIZE]; /*接收缓冲区*/ 142 | unsigned int in_size; /*接收缓冲区的大小*/ 143 | char out_buffer[RTSP_BUFFERSIZE + MAX_DESCR_LENGTH]; /*发送缓冲区*/ 144 | int out_size; /*发送缓冲区大小*/ 145 | 146 | unsigned int rtsp_cseq; /*序列号*/ 147 | char descr[MAX_DESCR_LENGTH]; /*描述*/ 148 | RTSP_session *session_list; /*会话链表*/ 149 | struct _RTSP_buffer *next; /*指向下一个结构体,构成了链表结构*/ 150 | } RTSP_buffer; 151 | 152 | /* tcp相关 */ 153 | char *sock_ntop_host( 154 | const struct sockaddr *sa, socklen_t salen, char *str, size_t len); 155 | int tcp_accept(int fd); 156 | int tcp_connect(unsigned short port, char *addr); 157 | int tcp_listen(unsigned short port); 158 | int tcp_read(int fd, void *buffer, int nbytes, struct sockaddr *Addr); 159 | int tcp_send(int fd, void *dataBuf, unsigned int dataSize); 160 | int tcp_write(int fd, char *buffer, int nbytes); 161 | 162 | /* schedule相关 */ 163 | #define MAX_PROCESS 1 /*number of fork*/ 164 | #define MAX_CONNECTION 10 /*rtsp connection*/ 165 | 166 | typedef struct _play_args { 167 | struct tm playback_time; /*回放时间*/ 168 | short playback_time_valid; /*回放时间是否合法*/ 169 | float start_time; /*开始时间*/ 170 | short start_time_valid; /*开始时间是否合法*/ 171 | float end_time; /*结束时间*/ 172 | } stPlayArgs; 173 | 174 | typedef unsigned int (*RTP_play_action)( 175 | unsigned int u32Rtp, char *pData, int s32DataSize, 176 | unsigned int u32TimeStamp); 177 | 178 | typedef struct _schedule_list { 179 | int valid; /*合法性标识*/ 180 | int BeginFrame; 181 | RTP_session *rtp_session; /*RTP会话*/ 182 | RTP_play_action play_action; /*播放动作*/ 183 | } stScheList; 184 | 185 | int ScheduleInit(); 186 | void *schedule_do(void *nothing); 187 | int schedule_add(RTP_session *rtp_session /*,RTSP_session *rtsp_session*/); 188 | int schedule_start(int id, stPlayArgs *args); 189 | void schedule_stop(int id); 190 | int schedule_remove(int id); 191 | int schedule_resume(int id, stPlayArgs *args); 192 | 193 | typedef enum { 194 | /*sender report,for transmission and reception statics from participants 195 | that are active senders*/ 196 | SR = 200, 197 | /*receiver report,for reception statistics from participants that are not 198 | active senders and in combination with SR for active senders reporting 199 | on more than 31 sources 200 | */ 201 | RR = 201, 202 | SDES = 202, /*Source description items, including CNAME,NAME,EMAIL,etc*/ 203 | BYE = 203, /*Indicates end of participation*/ 204 | APP = 204 /*Application-specific functions*/ 205 | } rtcp_pkt_type; 206 | 207 | /* Define default RTSP listening port */ 208 | #define SERVER_RTSP_PORT_DEFAULT 554 209 | 210 | typedef struct _StServPrefs { 211 | char hostname[256]; 212 | char serv_root[256]; 213 | char log[256]; 214 | unsigned int port; 215 | unsigned int max_session; 216 | } StServPrefs; 217 | 218 | int send_reply(int err, char *addon, RTSP_buffer *rtsp); 219 | int bwrite(char *buffer, unsigned short len, RTSP_buffer *rtsp); 220 | const char *get_stat(int err); 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /src/sensor.c: -------------------------------------------------------------------------------- 1 | #include "sensor.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | int (*sensor_register_callback_fn)(void); 15 | int (*sensor_unregister_callback_fn)(void); 16 | void *libsns_so = NULL; 17 | 18 | int tryLoadLibrary(const char *path) { 19 | printf("try to load: %s\n", path); 20 | libsns_so = dlopen(path, RTLD_NOW | RTLD_GLOBAL); 21 | printf("libsns_so 0x%016" PRIXPTR "\n", (uintptr_t)libsns_so); 22 | if (libsns_so == NULL) { 23 | printf("dlopen \"%s\" error: %s\n", path, dlerror()); 24 | return 0; 25 | } 26 | return 1; 27 | } 28 | int LoadSensorLibrary(const char *libsns_name) { 29 | char path[250]; 30 | sprintf(path, "%s", libsns_name); 31 | if (tryLoadLibrary(path) != 1) { 32 | sprintf(path, "./%s", libsns_name); 33 | if (tryLoadLibrary(path) != 1) { 34 | sprintf(path, "/usr/lib/%s", libsns_name); 35 | if (tryLoadLibrary(path) != 1) { 36 | return 0; 37 | } 38 | } 39 | } 40 | sensor_register_callback_fn = dlsym(libsns_so, "sensor_register_callback"); 41 | sensor_unregister_callback_fn = 42 | dlsym(libsns_so, "sensor_unregister_callback"); 43 | return 1; 44 | } 45 | 46 | void UnloadSensorLibrary() { 47 | dlclose(libsns_so); 48 | libsns_so = NULL; 49 | } 50 | 51 | int sensor_register_callback(void) { return sensor_register_callback_fn(); } 52 | int sensor_unregister_callback(void) { return sensor_unregister_callback_fn(); } 53 | -------------------------------------------------------------------------------- /src/sensor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern int (*sensor_register_callback_fn)(void); 4 | extern int (*sensor_unregister_callback_fn)(void); 5 | extern void *libsns_so; 6 | 7 | int tryLoadLibrary(const char *path); 8 | int LoadSensorLibrary(const char *libsns_name); 9 | void UnloadSensorLibrary(); 10 | 11 | int sensor_register_callback(void); 12 | int sensor_unregister_callback(void); 13 | -------------------------------------------------------------------------------- /src/server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | extern bool keepRunning; 6 | 7 | int start_server(); 8 | int stop_server(); 9 | 10 | void send_jpeg(uint8_t chn_index, char *buf, ssize_t size); 11 | void send_mjpeg(uint8_t chn_index, char *buf, ssize_t size); 12 | void send_h264_to_client(uint8_t chn_index, const void *p); 13 | void send_mp4_to_client(uint8_t chn_index, const void *p); 14 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if UINT32_MAX == UINTPTR_MAX 5 | #define STACK_CHK_GUARD 0xe2dee396 6 | #else 7 | #define STACK_CHK_GUARD 0x595e9fbd94fda766 8 | #endif 9 | 10 | uintptr_t __stack_chk_guard = STACK_CHK_GUARD; 11 | 12 | __attribute__((noreturn)) void __stack_chk_fail(void) { 13 | #if __STDC_HOSTED__ 14 | abort(); 15 | #elif __is_myos_kernel 16 | panic("Stack smashing detected"); 17 | #endif 18 | } 19 | -------------------------------------------------------------------------------- /src/tools.c: -------------------------------------------------------------------------------- 1 | #include "tools.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const char *getExt(const char *path) { 10 | const char *dot = strrchr(path, '.'); 11 | if (!dot || dot == path) 12 | return ""; 13 | return dot + 1; 14 | } 15 | 16 | const char *getMime(const char *path) { 17 | const char *ext = getExt(path); 18 | if (strlen(ext) == 0) 19 | return ""; 20 | if (strncmp(ext, "html", strlen("html")) == 0) { 21 | return "text/html"; 22 | } else if (strncmp(ext, "css", strlen("css")) == 0) { 23 | return "text/css"; 24 | } else if (strncmp(ext, "js", strlen("js")) == 0) { 25 | return "application/javascript"; 26 | } else if (strncmp(ext, "json", strlen("json")) == 0) { 27 | return "application/json"; 28 | } else if (strncmp(ext, "jpg", strlen("jpg")) == 0) { 29 | return "image/jpeg"; 30 | } else if (strncmp(ext, "jpeg", strlen("jpeg")) == 0) { 31 | return "image/jpeg"; 32 | } else if (strncmp(ext, "gif", strlen("gif")) == 0) { 33 | return "image/gif"; 34 | } else if (strncmp(ext, "png", strlen("png")) == 0) { 35 | return "image/png"; 36 | } else if (strncmp(ext, "svg", strlen("svg")) == 0) { 37 | return "image/svg+xml"; 38 | } else if (strncmp(ext, "mp4", strlen("mp4")) == 0) { 39 | return "video/mp4"; 40 | } 41 | return ""; 42 | } 43 | 44 | #define MAX_ERROR_MSG 0x1000 45 | int compile_regex(regex_t *r, const char *regex_text) { 46 | int status = regcomp(r, regex_text, REG_EXTENDED | REG_NEWLINE | REG_ICASE); 47 | if (status != 0) { 48 | char error_message[MAX_ERROR_MSG]; 49 | regerror(status, r, error_message, MAX_ERROR_MSG); 50 | printf("Regex error compiling '%s': %s\n", regex_text, error_message); 51 | fflush(stdout); 52 | return -1; 53 | } 54 | return 1; 55 | } 56 | 57 | int parseRequestPath(const char *headers, char *path) { 58 | regex_t regex; 59 | compile_regex(®ex, "^GET (/.*) HTTP"); 60 | size_t n_matches = 2; // We have 1 capturing group + the whole match group 61 | regmatch_t m[n_matches]; 62 | const char *p = headers; 63 | int match = regexec(®ex, p, n_matches, m, 0); 64 | regfree(®ex); 65 | if (match) { 66 | return -1; 67 | } 68 | sprintf( 69 | path, ".%.*s", (int)(m[1].rm_eo - m[1].rm_so), &headers[m[1].rm_so]); 70 | return 1; 71 | } 72 | 73 | /* aaaack but it's fast and const should make it shared text page. */ 74 | static const unsigned char pr2six[256] = { 75 | /* ASCII table */ 76 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 77 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 78 | 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 79 | 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 80 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 81 | 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 82 | 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 83 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 84 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 85 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 86 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 87 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 88 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 89 | 64, 64, 64, 64, 64, 64, 64, 64, 64}; 90 | 91 | static const char basis_64[] = 92 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 93 | 94 | int Base64encode_len(int len) { return ((len + 2) / 3 * 4) + 1; } 95 | 96 | int Base64encode(char *encoded, const char *string, int len) { 97 | int i; 98 | char *p; 99 | 100 | p = encoded; 101 | for (i = 0; i < len - 2; i += 3) { 102 | *p++ = basis_64[(string[i] >> 2) & 0x3F]; 103 | *p++ = basis_64 104 | [((string[i] & 0x3) << 4) | ((int)(string[i + 1] & 0xF0) >> 4)]; 105 | *p++ = basis_64 106 | [((string[i + 1] & 0xF) << 2) | ((int)(string[i + 2] & 0xC0) >> 6)]; 107 | *p++ = basis_64[string[i + 2] & 0x3F]; 108 | } 109 | if (i < len) { 110 | *p++ = basis_64[(string[i] >> 2) & 0x3F]; 111 | if (i == (len - 1)) { 112 | *p++ = basis_64[((string[i] & 0x3) << 4)]; 113 | *p++ = '='; 114 | } else { 115 | *p++ = basis_64 116 | [((string[i] & 0x3) << 4) | ((int)(string[i + 1] & 0xF0) >> 4)]; 117 | *p++ = basis_64[((string[i + 1] & 0xF) << 2)]; 118 | } 119 | *p++ = '='; 120 | } 121 | 122 | *p++ = '\0'; 123 | return p - encoded; 124 | } 125 | 126 | bool startsWith(const char *str, const char *prefix) { 127 | size_t lenpre = strlen(prefix), lenstr = strlen(str); 128 | return lenstr < lenpre ? false : memcmp(prefix, str, lenpre) == 0; 129 | } 130 | 131 | bool get_uint64(char *str, char *pattern, uint64_t *value) { 132 | char reg_buf[128]; 133 | ssize_t reg_buf_len = sprintf(reg_buf, "%s([0-9]+)", pattern); 134 | reg_buf[reg_buf_len] = 0; 135 | 136 | regex_t regex; 137 | if (compile_regex(®ex, reg_buf) < 0) { 138 | printf("get_uint64 compile_regex error\n"); 139 | return false; 140 | }; 141 | size_t n_matches = 2; // We have 1 capturing group + the whole match group 142 | regmatch_t m[n_matches]; 143 | 144 | char value_str[16] = {0}; 145 | int match = regexec(®ex, str, n_matches, m, 0); 146 | if (match != 0) 147 | return false; 148 | int len = sprintf( 149 | value_str, "%.*s", (int)(m[1].rm_eo - m[1].rm_so), str + m[1].rm_so); 150 | value_str[len] = 0; 151 | 152 | char *pEnd; 153 | *value = strtoll(value_str, &pEnd, 10); 154 | regfree(®ex); 155 | 156 | return true; 157 | } 158 | bool get_uint32(char *str, char *pattern, uint32_t *value) { 159 | uint64_t val64 = 0; 160 | if (!get_uint64(str, pattern, &val64)) 161 | return false; 162 | *value = val64; 163 | return true; 164 | } 165 | bool get_uint16(char *str, char *pattern, uint16_t *value) { 166 | uint64_t val64 = 0; 167 | if (!get_uint64(str, pattern, &val64)) 168 | return false; 169 | *value = val64; 170 | return true; 171 | } 172 | bool get_uint8(char *str, char *pattern, uint8_t *value) { 173 | uint64_t val64 = 0; 174 | if (!get_uint64(str, pattern, &val64)) 175 | return false; 176 | *value = val64; 177 | return true; 178 | } 179 | -------------------------------------------------------------------------------- /src/tools.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | const char *getMime(const char *path); 8 | int parseRequestPath(const char *headers, char *path); 9 | 10 | int compile_regex(regex_t *r, const char *regex_text); 11 | 12 | int Base64encode_len(int len); 13 | int Base64encode(char *encoded, const char *string, int len); 14 | 15 | bool get_uint64(char *str, char *pattern, uint64_t *value); 16 | bool get_uint32(char *str, char *pattern, uint32_t *value); 17 | bool get_uint16(char *str, char *pattern, uint16_t *value); 18 | bool get_uint8(char *str, char *pattern, uint8_t *value); 19 | 20 | bool startsWith(const char *str, const char *prefix); 21 | -------------------------------------------------------------------------------- /src/videohw.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct SDKState { 16 | ISP_DEV isp_dev; 17 | VI_DEV vi_dev; 18 | VI_CHN vi_chn; 19 | 20 | MD_CHN md_chn; 21 | }; 22 | extern struct SDKState state; 23 | 24 | int start_sdk(); 25 | int stop_sdk(); 26 | 27 | void set_color2gray(bool color2gray); 28 | 29 | uint32_t take_next_free_channel(bool in_main_loop); 30 | bool channel_is_enable(uint32_t channel); 31 | bool channel_main_loop(uint32_t channel); 32 | void set_channel_disable(uint32_t channel); 33 | 34 | HI_S32 bind_vpss_venc(VENC_CHN venc_chn); 35 | HI_S32 unbind_vpss_venc(VENC_CHN venc_chn); 36 | 37 | HI_S32 create_venc_chn(VENC_CHN venc_chn, uint32_t fps_src, uint32_t fps_dst); 38 | HI_S32 disable_venc_chn(VENC_CHN venc_chn); 39 | -------------------------------------------------------------------------------- /tools/cmake/toolchains/arm-openipc-linux-musleabi.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Linux) 2 | set(CMAKE_CROSSCOMPILING TRUE) 3 | set(CMAKE_SYSTEM_PROCESSOR arm) 4 | 5 | # Specify the cross compiler. 6 | set(CMAKE_C_COMPILER arm-openipc-linux-musleabi-gcc CACHE FILEPATH "C compiler") 7 | set(CMAKE_CXX_COMPILER arm-openipc-linux-musleabi-g++ CACHE FILEPATH "C++ compiler") 8 | set(CMAKE_ASM_COMPILER arm-openipc-linux-musleabi-gcc CACHE FILEPATH "ASM compiler") 9 | 10 | # Search libraries only under *target* paths. 11 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 14 | --------------------------------------------------------------------------------