mirror of
https://github.com/sipeed/MaixCDK.git
synced 2026-09-10 21:59:54 -05:00
* update for ax630c
This commit is contained in:
11
components/3rd_party/FFmpeg/CMakeLists.txt
vendored
11
components/3rd_party/FFmpeg/CMakeLists.txt
vendored
@@ -1,6 +1,10 @@
|
||||
set(ffmpeg_version_str "${CONFIG_FFMPEG_VERSION_MAJOR}.${CONFIG_FFMPEG_VERSION_MINOR}.${CONFIG_FFMPEG_VERSION_PATCH}")
|
||||
set(ffmpeg_unzip_path "${DL_EXTRACTED_PATH}/ffmpeg_srcs")
|
||||
set(src_path "${ffmpeg_unzip_path}/ffmpeg")
|
||||
if(PLATFORM_MAIXCAM2)
|
||||
set(src_path "${ffmpeg_unzip_path}/ffmpeg_ax630c_libs")
|
||||
else()
|
||||
set(src_path "${ffmpeg_unzip_path}/ffmpeg")
|
||||
endif()
|
||||
############### Add include ###################
|
||||
set(ffmpeg_include_dir "${src_path}/include"
|
||||
"."
|
||||
@@ -62,12 +66,13 @@ set(ffmpeg_dynamic_lib_file ${src_path}/lib/libavcodec.so
|
||||
${src_path}/lib/libavformat.so
|
||||
${src_path}/lib/libavresample.so
|
||||
${src_path}/lib/libavutil.so
|
||||
${src_path}/lib/libpostproc.so
|
||||
${src_path}/lib/libswresample.so
|
||||
${src_path}/lib/libswscale.so
|
||||
)
|
||||
list(APPEND ADD_DYNAMIC_LIB ${ffmpeg_dynamic_lib_file})
|
||||
list(APPEND ADD_DIST_LIB_IGNORE ${ffmpeg_dynamic_lib_file})
|
||||
if (PLATFORM_MAIXCAM)
|
||||
list(APPEND ADD_DYNAMIC_LIB ${src_path}/lib/libpostproc.so)
|
||||
endif()
|
||||
set_property(SOURCE ${ffmpeg_dynamic_lib_file} PROPERTY GENERATED 1)
|
||||
###############################################
|
||||
|
||||
|
||||
80
components/3rd_party/FFmpeg/component.py
vendored
80
components/3rd_party/FFmpeg/component.py
vendored
@@ -5,32 +5,60 @@ def add_file_downloads(confs : dict) -> list:
|
||||
@return list type, items is dict type
|
||||
'''
|
||||
version = f"{confs['CONFIG_FFMPEG_VERSION_MAJOR']}.{confs['CONFIG_FFMPEG_VERSION_MINOR']}.{confs['CONFIG_FFMPEG_VERSION_PATCH']}.{confs['CONFIG_FFMPEG_COMPILED_VERSION']}"
|
||||
url = f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/ffmpeg_libs_n{version}.tar.xz"
|
||||
if version == "4.4.4.1":
|
||||
sha256sum = "d4b8eb70a7864c855e8e286fef39b9b90faf72c1b77491148c451da252cad6c0"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = f"ffmpeg_libs_n{version}.tar.xz"
|
||||
path = f"ffmpeg_srcs"
|
||||
check_file = 'ffmpeg'
|
||||
rename = {
|
||||
f'ffmpeg_libs_n{version}': 'ffmpeg'
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
],
|
||||
'rename': rename
|
||||
if confs.get('PLATFORM_MAIXCAM', None):
|
||||
url = f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/ffmpeg_libs_n{version}.tar.xz"
|
||||
if version == "4.4.4.1":
|
||||
sha256sum = "d4b8eb70a7864c855e8e286fef39b9b90faf72c1b77491148c451da252cad6c0"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = f"ffmpeg_libs_n{version}.tar.xz"
|
||||
path = f"ffmpeg_srcs"
|
||||
check_file = 'ffmpeg'
|
||||
rename = {
|
||||
f'ffmpeg_libs_n{version}': 'ffmpeg'
|
||||
}
|
||||
]
|
||||
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
],
|
||||
'rename': rename
|
||||
}
|
||||
]
|
||||
elif confs.get('PLATFORM_MAIXCAM2', None):
|
||||
url = f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/ffmpeg_ax630c_libs_n{version}.tar.xz"
|
||||
if version == "4.4.4.1":
|
||||
sha256sum = "3fcc394fad917127dd9503396dd84ce29fe47f33e76ebfd86dce6a73031e3a4b."
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = f"ffmpeg_ax630c_libs_n{version}.tar.xz"
|
||||
path = f"ffmpeg_srcs"
|
||||
check_file = 'ffmpeg_ax630c_libs'
|
||||
rename = {
|
||||
f'ffmpeg_ax630c_libs_n{version}': 'ffmpeg_ax630c_libs'
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
],
|
||||
'rename': rename
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
121
components/3rd_party/ax620e_bsp_sdk/CMakeLists.txt
vendored
Normal file
121
components/3rd_party/ax620e_bsp_sdk/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
set(srcs_path "${DL_EXTRACTED_PATH}/ax620e_bsp_sdk_srcs/ax620e_bsp_sdk")
|
||||
################# Add include #################
|
||||
set(bsp_glibc_path "${srcs_path}/msp/out/arm64_glibc")
|
||||
set(bsp_sample_path "${srcs_path}/msp/sample")
|
||||
set(bsp_app_path "${srcs_path}/app")
|
||||
list(APPEND ADD_INCLUDE "${bsp_glibc_path}/include"
|
||||
"${bsp_sample_path}/common"
|
||||
)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "${bsp_app_path}/component/3rd/inc/appweb")
|
||||
###############################################
|
||||
|
||||
############## Add source files ###############
|
||||
# list(APPEND ADD_SRCS "src/lib1.c"
|
||||
# )
|
||||
# aux_source_directory("inifile2/src" ADD_SRCS) # collect all source file in src dir, will set var ADD_SRCS
|
||||
append_srcs_dir(ADD_SRCS "${bsp_sample_path}/common") # append source file in src dir to var ADD_SRCS
|
||||
# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c")
|
||||
###############################################
|
||||
|
||||
###### Add required/dependent components ######
|
||||
# list(APPEND ADD_REQUIREMENTS component1)
|
||||
###############################################
|
||||
|
||||
###### Add link search path for requirements/libs ######
|
||||
# list(APPEND ADD_LINK_SEARCH_PATH "${CONFIG_TOOLCHAIN_PATH}/lib")
|
||||
# list(APPEND ADD_REQUIREMENTS m) # add system libs, pthread or m(math) lib for example
|
||||
###############################################
|
||||
|
||||
############ Add static libs ##################
|
||||
# if(CONFIG_COMPONENT1_INCLUDE_STATIC_LIB)
|
||||
|
||||
# endif()
|
||||
###############################################
|
||||
|
||||
############ Add dynamic libs ##################
|
||||
list(APPEND ADD_DYNAMIC_LIB "${bsp_glibc_path}/lib/libax_skel.so"
|
||||
"${bsp_glibc_path}/lib/libax_skel.so"
|
||||
"${bsp_glibc_path}/lib/libax_avs.so"
|
||||
"${bsp_glibc_path}/lib/libax_utility.so"
|
||||
"${bsp_glibc_path}/lib/libax_af.so"
|
||||
"${bsp_glibc_path}/lib/libax_venc.so"
|
||||
# "${bsp_glibc_path}/lib/libax_faac.so"
|
||||
"${bsp_glibc_path}/lib/libax_proton.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc500ai.so"
|
||||
"${bsp_glibc_path}/lib/libax_nt_stream.so"
|
||||
"${bsp_glibc_path}/lib/libax_audio_3a.so"
|
||||
"${bsp_glibc_path}/lib/libax_interpreter.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc231ai.so"
|
||||
"${bsp_glibc_path}/lib/libax_ivps.so"
|
||||
"${bsp_glibc_path}/lib/libax_ives.so"
|
||||
# "${bsp_glibc_path}/lib/libax_audio.so"
|
||||
"${bsp_glibc_path}/lib/libax_ae.so"
|
||||
"${bsp_glibc_path}/lib/libax_skel_tiny.so"
|
||||
"${bsp_glibc_path}/lib/libsns_imx664.so"
|
||||
"${bsp_glibc_path}/lib/libax_efuse.so"
|
||||
"${bsp_glibc_path}/lib/libax_vo.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc430ai.so"
|
||||
"${bsp_glibc_path}/lib/libsns_s5kjn1sq03.so"
|
||||
# "${bsp_glibc_path}/lib/libax_fdk.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc450ai.so"
|
||||
"${bsp_glibc_path}/lib/libax_sys.so"
|
||||
"${bsp_glibc_path}/lib/libax_vdec.so"
|
||||
"${bsp_glibc_path}/lib/libax_awb.so"
|
||||
"${bsp_glibc_path}/lib/libax_mipi.so"
|
||||
"${bsp_glibc_path}/lib/liblens_t5719.so"
|
||||
"${bsp_glibc_path}/lib/libsns_imx678.so"
|
||||
"${bsp_glibc_path}/lib/libax_ive.so"
|
||||
"${bsp_glibc_path}/lib/libax_engine.so"
|
||||
"${bsp_glibc_path}/lib/libsns_os04d10.so"
|
||||
"${bsp_glibc_path}/lib/libsns_mis2032.so"
|
||||
"${bsp_glibc_path}/lib/libax_nt_ctrl.so"
|
||||
"${bsp_glibc_path}/lib/libsns_os08c10.so"
|
||||
"${bsp_glibc_path}/lib/libax_avscali.so"
|
||||
# "${bsp_glibc_path}/lib/libax_opus.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc200ai.so"
|
||||
"${bsp_glibc_path}/lib/libsns_dummy.so"
|
||||
"${bsp_glibc_path}/lib/libsns_os04a10.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc530ai.so"
|
||||
"${bsp_glibc_path}/lib/libax_engine_tiny.so"
|
||||
"${bsp_glibc_path}/lib/libsns_gc4653.so"
|
||||
"${bsp_glibc_path}/lib/libax_gzipd.so"
|
||||
"${bsp_glibc_path}/lib/libax_thermal.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc4210.so"
|
||||
"${bsp_glibc_path}/lib/libsns_gc4663.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc830ai.so"
|
||||
"${bsp_glibc_path}/lib/libsns_c4395.so"
|
||||
"${bsp_glibc_path}/lib/libax_cipher.so"
|
||||
# "${bsp_glibc_path}/lib/libax_opal.so"
|
||||
"${bsp_glibc_path}/lib/libsns_sc850sl.so"
|
||||
"${bsp_glibc_path}/lib/libax_syslog.so"
|
||||
"${bsp_glibc_path}/lib/libsns_dummy_bittrue.so"
|
||||
"${bsp_glibc_path}/lib/liblens_dciris.so"
|
||||
# "${srcs_path}/third-party/faac/lib/arm64/glibc/libfaac.so.0"
|
||||
"${srcs_path}/third-party/fdk-aac/lib/arm64/glibc/libfdk-aac.so.2"
|
||||
"${srcs_path}/third-party/opus/lib/arm64/glibc/libopus.so.0"
|
||||
"${bsp_app_path}/component/resource/lib/arm64/glibc/appweb/libmpr.so"
|
||||
|
||||
)
|
||||
|
||||
###############################################
|
||||
|
||||
#### Add compile option for this component ####
|
||||
#### Just for this component, won't affect other
|
||||
#### modules, including component that depend
|
||||
#### on this component
|
||||
# list(APPEND ADD_DEFINITIONS_PRIVATE -DAAAAA=1)
|
||||
|
||||
#### Add compile option for this component
|
||||
#### Add components that depend on this component
|
||||
# list(APPEND ADD_DEFINITIONS -DAAAAA222=1
|
||||
# -DAAAAA333=1)
|
||||
###############################################
|
||||
|
||||
############ Add static libs ##################
|
||||
#### Update parent's variables like CMAKE_C_LINK_FLAGS
|
||||
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,-rpath,${srcs_path}/third-party/faac/lib/arm/glibc/libfaac.so.0" PARENT_SCOPE)
|
||||
###############################################
|
||||
|
||||
# register component, DYNAMIC or SHARED flags will make component compiled to dynamic(shared) lib
|
||||
register_component()
|
||||
|
||||
36
components/3rd_party/ax620e_bsp_sdk/component.py
vendored
Normal file
36
components/3rd_party/ax620e_bsp_sdk/component.py
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
def add_file_downloads(confs : dict) -> list:
|
||||
'''
|
||||
@param confs kconfig vars, dict type
|
||||
@return list type, items is dict type
|
||||
'''
|
||||
return [
|
||||
{
|
||||
'url': f"https://github.com/AXERA-TECH/ax620e_bsp_sdk/archive/refs/tags/v2.0.0_P7.tar.gz",
|
||||
'urls': [],
|
||||
'sites': ["https://github.com/AXERA-TECH/ax620e_bsp_sdk"],
|
||||
'sha256sum': "adfe5fda41b1aac131a2b2618a823b9c5041c486dfa725ce7fc0e9c1331cca65",
|
||||
'filename': f"ax620e_bsp_sdk-2.0.0_P7.tar.gz",
|
||||
'path': f"ax620e_bsp_sdk_srcs",
|
||||
'check_files': [
|
||||
'ax620e_bsp_sdk'
|
||||
],
|
||||
'rename': {
|
||||
f'ax620e_bsp_sdk-2.0.0_P7': 'ax620e_bsp_sdk'
|
||||
}
|
||||
},
|
||||
{
|
||||
'url': f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/ax630c-third-party_2.0.0.tar.gz",
|
||||
'urls': [],
|
||||
'sites': ["https://github.com/AXERA-TECH/ax620e_bsp_sdk"],
|
||||
'sha256sum': "9c9b25aec80e1195fabe35b29f74ca588f6dec0370dfa7f2c6f1c5020ee89547",
|
||||
'filename': f"ax630c-third-party_2.0.0.tar.gz",
|
||||
'path': f"ax620e_bsp_sdk_srcs/ax620e_bsp_sdk",
|
||||
'check_files': [
|
||||
'ax620e_bsp_sdk_srcs/ax620e_bsp_sdk/third-party'
|
||||
],
|
||||
'rename': {}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
2
components/3rd_party/brotli/CMakeLists.txt
vendored
2
components/3rd_party/brotli/CMakeLists.txt
vendored
@@ -96,7 +96,7 @@ endif()
|
||||
|
||||
if(PLATFORM_M2DOCK)
|
||||
set(brotli_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_m2dock.cmake)
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
set(brotli_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux_cross.cmake)
|
||||
elseif(PLATFORM_LINUX)
|
||||
set(brotli_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux.cmake)
|
||||
|
||||
2
components/3rd_party/freetype/CMakeLists.txt
vendored
2
components/3rd_party/freetype/CMakeLists.txt
vendored
@@ -91,7 +91,7 @@ set(freetype_unzip_path "${DL_EXTRACTED_PATH}/freetype_srcs")
|
||||
set(freetype_src_path "${freetype_unzip_path}/freetype-${freetype_version_str}")
|
||||
if(PLATFORM_M2DOCK)
|
||||
set(freetype_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_m2dock.cmake)
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
set(freetype_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux_cross.cmake)
|
||||
elseif(PLATFORM_LINUX)
|
||||
set(freetype_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux.cmake)
|
||||
|
||||
10
components/3rd_party/harfbuzz/CMakeLists.txt
vendored
10
components/3rd_party/harfbuzz/CMakeLists.txt
vendored
@@ -83,6 +83,14 @@ if(PLATFORM_MAIXCAM AND NOT CONFIG_COMPONENTS_COMPILE_FROM_SOURCE)
|
||||
list(APPEND ADD_INCLUDE ${harfbuzz_include_dir})
|
||||
list(APPEND ADD_STATIC_LIB ${harfbuzz_lib_file})
|
||||
endif()
|
||||
elseif(PLATFORM_MAIXCAM2 AND NOT CONFIG_COMPONENTS_COMPILE_FROM_SOURCE)
|
||||
set(harfbuzz_lib_dir "${DL_EXTRACTED_PATH}/harfbuzz/harfbuzz_maixcam2_glibc_v8.2.1")
|
||||
set(harfbuzz_include_dir "${harfbuzz_lib_dir}/include/harfbuzz" "${harfbuzz_lib_dir}/include")
|
||||
set(harfbuzz_lib_file "${harfbuzz_lib_dir}/lib/libharfbuzz.a")
|
||||
set_property(SOURCE ${harfbuzz_lib_file} PROPERTY GENERATED 1)
|
||||
set_property(SOURCE ${harfbuzz_include_dir} PROPERTY GENERATED 1)
|
||||
list(APPEND ADD_INCLUDE ${harfbuzz_include_dir})
|
||||
list(APPEND ADD_STATIC_LIB ${harfbuzz_lib_file})
|
||||
endif()
|
||||
|
||||
if(NOT ADD_INCLUDE)
|
||||
@@ -103,7 +111,7 @@ if(NOT ADD_INCLUDE)
|
||||
|
||||
if(PLATFORM_M2DOCK)
|
||||
set(harfbuzz_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_m2dock.cmake)
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
set(harfbuzz_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux_cross.cmake)
|
||||
elseif(PLATFORM_LINUX)
|
||||
set(harfbuzz_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux.cmake)
|
||||
|
||||
80
components/3rd_party/harfbuzz/component.py
vendored
80
components/3rd_party/harfbuzz/component.py
vendored
@@ -4,7 +4,7 @@ def add_file_downloads(confs : dict) -> list:
|
||||
@param confs kconfig vars, dict type
|
||||
@return list type, items is dict type
|
||||
'''
|
||||
if (not confs.get("PLATFORM_MAIXCAM", None)) or confs.get("CONFIG_COMPONENTS_COMPILE_FROM_SOURCE", None):
|
||||
if not (bool(confs.get("PLATFORM_MAIXCAM", None)) or bool(confs.get('PLATFORM_MAIXCAM2', None))) or confs.get("CONFIG_COMPONENTS_COMPILE_FROM_SOURCE", None):
|
||||
# harfbuzz src
|
||||
version = f"{confs['CONFIG_HARFBUZZ_VERSION_MAJOR']}.{confs['CONFIG_HARFBUZZ_VERSION_MINOR']}.{confs['CONFIG_HARFBUZZ_VERSION_PATCH']}"
|
||||
url = f"https://github.com/harfbuzz/harfbuzz/releases/download/{version}/harfbuzz-{version}.tar.xz"
|
||||
@@ -16,6 +16,56 @@ def add_file_downloads(confs : dict) -> list:
|
||||
filename = f"harfbuzz-{version}.tar.xz"
|
||||
path = "harfbuzz_srcs"
|
||||
check_file = f'harfbuzz-{version}'
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
]
|
||||
}
|
||||
]
|
||||
elif confs.get('PLATFORM_MAIXCAM', None):
|
||||
if "musl" not in confs["CONFIG_TOOLCHAIN_PATH"]:
|
||||
return []
|
||||
version = "8.2.1"
|
||||
url = "https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/harfbuzz_maixcam_musl_v8.2.1.tar.xz"
|
||||
if version == "8.2.1":
|
||||
sha256sum = "0ee057914aeabd4d44c23f776e3dde79a3ec0a34cfe5e0a9c09737d0bf34e7fb"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = "harfbuzz_maixcam_musl_v8.2.1.tar.xz"
|
||||
path = "harfbuzz"
|
||||
check_file = 'harfbuzz_maixcam_musl_v8.2.1'
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
]
|
||||
}
|
||||
]
|
||||
elif confs.get('PLATFORM_MAIXCAM2', None):
|
||||
version = "8.2.1"
|
||||
url = "https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/harfbuzz_maixcam2_glibc_v8.2.1.tar.xz"
|
||||
if version == "8.2.1":
|
||||
sha256sum = "16cb4a733b17370cbb012cca16867a57a6080a40251be51f18f40164b60ff8b3"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = "harfbuzz_maixcam2_glibc_v8.2.1.tar.xz"
|
||||
path = "harfbuzz"
|
||||
check_file = 'harfbuzz_maixcam2_glibc_v8.2.1'
|
||||
|
||||
return [
|
||||
{
|
||||
@@ -31,32 +81,4 @@ def add_file_downloads(confs : dict) -> list:
|
||||
}
|
||||
]
|
||||
|
||||
# maixcam precompiled lib
|
||||
if "musl" not in confs["CONFIG_TOOLCHAIN_PATH"]:
|
||||
return []
|
||||
version = "8.2.1"
|
||||
url = "https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/harfbuzz_maixcam_musl_v8.2.1.tar.xz"
|
||||
if version == "8.2.1":
|
||||
sha256sum = "0ee057914aeabd4d44c23f776e3dde79a3ec0a34cfe5e0a9c09737d0bf34e7fb"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = "harfbuzz_maixcam_musl_v8.2.1.tar.xz"
|
||||
path = "harfbuzz"
|
||||
check_file = 'harfbuzz_maixcam_musl_v8.2.1'
|
||||
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
|
||||
4
components/3rd_party/omv/CMakeLists.txt
vendored
4
components/3rd_party/omv/CMakeLists.txt
vendored
@@ -108,7 +108,9 @@ set_property(SOURCE "${src_path}/alloc" PROPERTY GENERATED 1)
|
||||
#### modules, including component that depend
|
||||
#### on this component
|
||||
list(APPEND ADD_DEFINITIONS_PRIVATE -w)
|
||||
|
||||
if(PLATFORM_MAIXCAM2)
|
||||
list(APPEND ADD_DEFINITIONS_PRIVATE -D__ARM_ARCH=0)
|
||||
endif()
|
||||
#### Add compile option for this component
|
||||
#### Add components that depend on this component
|
||||
# list(APPEND ADD_DEFINITIONS -DAAAAA222=1
|
||||
|
||||
16
components/3rd_party/opencv/CMakeLists.txt
vendored
16
components/3rd_party/opencv/CMakeLists.txt
vendored
@@ -18,7 +18,7 @@ if((NOT CONFIG_OPENCV_COMPILE_FROM_SOURCE) AND PLATFORM_LINUX AND NOT CONFIG_COM
|
||||
message(FATAL_ERROR "can not find OpenCV locally, will download source code to compile, you can install it by 'sudo apt install libopencv-dev libopencv-contrib-dev', or select OPENCV_COMPILE_FROM_SOURCE option to compile it from source code")
|
||||
endif()
|
||||
endif()
|
||||
if(PLATFORM_MAIXCAM AND NOT CONFIG_OPENCV_COMPILE_FROM_SOURCE AND NOT CONFIG_COMPONENTS_COMPILE_FROM_SOURCE)
|
||||
if((PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2) AND NOT CONFIG_OPENCV_COMPILE_FROM_SOURCE AND NOT CONFIG_COMPONENTS_COMPILE_FROM_SOURCE)
|
||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/opencv-mobile")
|
||||
list(APPEND ADD_INCLUDE "opencv-mobile/include/opencv4")
|
||||
list(APPEND ADD_STATIC_LIB
|
||||
@@ -32,10 +32,14 @@ if(PLATFORM_MAIXCAM AND NOT CONFIG_OPENCV_COMPILE_FROM_SOURCE AND NOT CONFIG_COM
|
||||
list(APPEND ADD_REQUIREMENTS pthread dl)
|
||||
list(APPEND ADD_DEFINITIONS -fopenmp)
|
||||
else()
|
||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/opencv4_lib_maixcam")
|
||||
set(opencv_lib_dir "${CMAKE_CURRENT_LIST_DIR}/opencv4_lib_maixcam")
|
||||
elseif(CONFIG_TOOLCHAIN_PATH MATCHES "musl")
|
||||
set(opencv_lib_dir "${DL_EXTRACTED_PATH}/opencv/opencv4/opencv4_lib_maixcam_musl_4.9.0")
|
||||
if (PLATFORM_MAIXCAM)
|
||||
if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/opencv4_lib_maixcam")
|
||||
set(opencv_lib_dir "${CMAKE_CURRENT_LIST_DIR}/opencv4_lib_maixcam")
|
||||
elseif(CONFIG_TOOLCHAIN_PATH MATCHES "musl")
|
||||
set(opencv_lib_dir "${DL_EXTRACTED_PATH}/opencv/opencv4/opencv4_lib_maixcam_musl_4.9.0")
|
||||
endif()
|
||||
elseif(PLATFORM_MAIXCAM2)
|
||||
set(opencv_lib_dir "${DL_EXTRACTED_PATH}/opencv/opencv4/opencv4_lib_maixcam2_glibc_4.9.0")
|
||||
endif()
|
||||
if(opencv_lib_dir)
|
||||
list(APPEND ADD_INCLUDE "${opencv_lib_dir}/include/opencv4")
|
||||
@@ -96,7 +100,7 @@ if(NOT ADD_INCLUDE)
|
||||
set(opencv_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_m2dock.cmake)
|
||||
elseif(PLATFORM_LINUX)
|
||||
set(opencv_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux.cmake)
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
set(opencv_toolchain_file ${CMAKE_CURRENT_LIST_DIR}/toolchain_linux_cross.cmake)
|
||||
else()
|
||||
message(FATAL_ERROR "No opencv toolchain config for this board, please edit to add opencv support for this board")
|
||||
|
||||
89
components/3rd_party/opencv/component.py
vendored
89
components/3rd_party/opencv/component.py
vendored
@@ -4,7 +4,7 @@ def add_file_downloads(confs : dict) -> list:
|
||||
@param confs kconfig vars, dict type
|
||||
@return list type, items is dict type
|
||||
'''
|
||||
if (not confs.get("PLATFORM_MAIXCAM", None)) or confs.get("CONFIG_COMPONENTS_COMPILE_FROM_SOURCE", None) or confs.get("CONFIG_OPENCV_COMPILE_FROM_SOURCE", None):
|
||||
if not (bool(confs.get("PLATFORM_MAIXCAM", None)) or bool(confs.get('PLATFORM_MAIXCAM2', None))) or confs.get("CONFIG_COMPONENTS_COMPILE_FROM_SOURCE", None) or confs.get("CONFIG_OPENCV_COMPILE_FROM_SOURCE", None):
|
||||
# version = f"{confs['CONFIG_PYTHON_VERSION_MAJOR']}.{confs['CONFIG_PYTHON_VERSION_MINOR']}.{confs['CONFIG_PYTHON_VERSION_PATCH']}"
|
||||
version = "4.9.0"
|
||||
url = f"https://github.com/opencv/opencv/archive/{version}.zip"
|
||||
@@ -32,7 +32,7 @@ def add_file_downloads(confs : dict) -> list:
|
||||
'rename': rename
|
||||
}
|
||||
]
|
||||
if confs.get("PLATFORM_MAIXCAM", None):
|
||||
if confs.get("PLATFORM_MAIXCAM", None) or confs.get("PLATFORM_MAIXCAM2", None):
|
||||
version = "0.1.2a"
|
||||
url = f"https://github.com/opencv/ade/archive/v0.1.2a.zip"
|
||||
if version == "0.1.2a":
|
||||
@@ -91,34 +91,61 @@ def add_file_downloads(confs : dict) -> list:
|
||||
raise Exception("No opencv config for this board, please edit to add opencv support for this board")
|
||||
|
||||
return files
|
||||
elif confs.get("PLATFORM_MAIXCAM", None):
|
||||
if "musl" not in confs["CONFIG_TOOLCHAIN_PATH"]:
|
||||
return []
|
||||
# version = f"{confs['CONFIG_OMV_VERSION_MAJOR']}.{confs['CONFIG_OMV_VERSION_MINOR']}.{confs['CONFIG_OMV_VERSION_PATCH']}"
|
||||
version = "4.9.0"
|
||||
url = f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/opencv4_lib_maixcam_musl_4.9.0.tar.xz"
|
||||
if version == "4.9.0":
|
||||
sha256sum = "c4553fe39212b271724d2bb9394e8944678f846f4e58a578da1db3013cbc3dcf"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = f"opencv4_lib_maixcam_musl_4.9.0.tar.xz"
|
||||
path = f"opencv/opencv4"
|
||||
check_file = f'opencv4_lib_maixcam_musl_4.9.0'
|
||||
rename = {}
|
||||
|
||||
if "musl" not in confs["CONFIG_TOOLCHAIN_PATH"]:
|
||||
return []
|
||||
# version = f"{confs['CONFIG_OMV_VERSION_MAJOR']}.{confs['CONFIG_OMV_VERSION_MINOR']}.{confs['CONFIG_OMV_VERSION_PATCH']}"
|
||||
version = "4.9.0"
|
||||
url = f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/opencv4_lib_maixcam_musl_4.9.0.tar.xz"
|
||||
if version == "4.9.0":
|
||||
sha256sum = "c4553fe39212b271724d2bb9394e8944678f846f4e58a578da1db3013cbc3dcf"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = f"opencv4_lib_maixcam_musl_4.9.0.tar.xz"
|
||||
path = f"opencv/opencv4"
|
||||
check_file = f'opencv4_lib_maixcam_musl_4.9.0'
|
||||
rename = {}
|
||||
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
],
|
||||
'rename': rename
|
||||
}
|
||||
]
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
],
|
||||
'rename': rename
|
||||
}
|
||||
]
|
||||
elif confs.get("PLATFORM_MAIXCAM2", None):
|
||||
# version = f"{confs['CONFIG_OMV_VERSION_MAJOR']}.{confs['CONFIG_OMV_VERSION_MINOR']}.{confs['CONFIG_OMV_VERSION_PATCH']}"
|
||||
version = "4.9.0"
|
||||
url = f"https://github.com/sipeed/MaixCDK/releases/download/v0.0.0/opencv4_lib_maixcam2_glibc_4.9.0.tar.xz"
|
||||
if version == "4.9.0":
|
||||
sha256sum = "42e2827b632cdfcd168e4a3d77414ce57e0d9c3398893ac899635124d1579a53"
|
||||
else:
|
||||
raise Exception(f"version {version} not support")
|
||||
sites = ["https://github.com/sipeed/MaixCDK/releases/tag/v0.0.0"]
|
||||
filename = f"opencv4_lib_maixcam2_glibc_4.9.0.tar.xz"
|
||||
path = f"opencv/opencv4"
|
||||
check_file = f'opencv4_lib_maixcam2_glibc_4.9.0'
|
||||
rename = {}
|
||||
|
||||
return [
|
||||
{
|
||||
'url': f'{url}',
|
||||
'urls': [],
|
||||
'sites': sites,
|
||||
'sha256sum': sha256sum,
|
||||
'filename': filename,
|
||||
'path': path,
|
||||
'check_files': [
|
||||
check_file
|
||||
],
|
||||
'rename': rename
|
||||
}
|
||||
]
|
||||
|
||||
5
components/3rd_party/sdl/CMakeLists.txt
vendored
5
components/3rd_party/sdl/CMakeLists.txt
vendored
@@ -95,6 +95,11 @@ if(NOT ADD_INCLUDE)
|
||||
string(LENGTH "${CONFIG_TOOLCHAIN_PREFIX}" toolchain_prefix_len)
|
||||
math(EXPR toolchain_prefix_len "${toolchain_prefix_len} - 1")
|
||||
string(SUBSTRING "${CONFIG_TOOLCHAIN_PREFIX}" 0 ${toolchain_prefix_len} host_prefix)
|
||||
elseif(PLATFORM_MAIXCAM2)
|
||||
set(arch "aarch64")
|
||||
string(LENGTH "${CONFIG_TOOLCHAIN_PREFIX}" toolchain_prefix_len)
|
||||
math(EXPR toolchain_prefix_len "${toolchain_prefix_len} - 1")
|
||||
string(SUBSTRING "${CONFIG_TOOLCHAIN_PREFIX}" 0 ${toolchain_prefix_len} host_prefix)
|
||||
else()
|
||||
message(FATAL_ERROR "please edit here to add support for this board")
|
||||
endif()
|
||||
|
||||
@@ -19,7 +19,7 @@ append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_S
|
||||
if (PLATFORM_LINUX)
|
||||
append_srcs_dir(ADD_SRCS "port/linux_common")
|
||||
append_srcs_dir(ADD_SRCS "port/linux")
|
||||
elseif (PLATFORM_MAIXCAM)
|
||||
elseif (PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
append_srcs_dir(ADD_SRCS "port/linux_common")
|
||||
append_srcs_dir(ADD_SRCS "port/maixcam")
|
||||
endif()
|
||||
|
||||
@@ -9,6 +9,9 @@ if(PLATFORM_LINUX)
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/maixcam")
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/linux_common")
|
||||
elseif(PLATFORM_MAIXCAM2)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/maixcam2")
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/linux_common")
|
||||
endif()
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "include_private")
|
||||
###############################################
|
||||
@@ -28,6 +31,9 @@ if(PLATFORM_LINUX)
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
append_srcs_dir(ADD_SRCS "port/maixcam")
|
||||
append_srcs_dir(ADD_SRCS "port/linux_common")
|
||||
elseif(PLATFORM_MAIXCAM2)
|
||||
append_srcs_dir(ADD_SRCS "port/maixcam2")
|
||||
append_srcs_dir(ADD_SRCS "port/linux_common")
|
||||
endif()
|
||||
# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c")
|
||||
# set(ADD_ASM_SRCS "src/asm.S")
|
||||
@@ -48,6 +54,8 @@ elseif(PLATFORM_MAIXCAM)
|
||||
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -Wl,--start-group vision/libvision.a -lmaixcam_lib -Wl,--end-group" PARENT_SCOPE)
|
||||
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -Wl,--start-group vision/libvision.a -lmaixcam_lib -Wl,--end-group" PARENT_SCOPE)
|
||||
endif()
|
||||
elseif(PLATFORM_MAIXCAM2)
|
||||
list(APPEND ADD_REQUIREMENTS FFmpeg RtspServer)
|
||||
endif()
|
||||
list(APPEND ADD_REQUIREMENTS eigen voice)
|
||||
###############################################
|
||||
|
||||
656
components/vision/port/maixcam2/maix_camera_maixcam2.cpp
Normal file
656
components/vision/port/maixcam2/maix_camera_maixcam2.cpp
Normal file
@@ -0,0 +1,656 @@
|
||||
/**
|
||||
* @author neucrack@sipeed, lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
|
||||
#include "maix_camera.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
#include "maix_i2c.hpp"
|
||||
#include <dirent.h>
|
||||
|
||||
namespace maix::camera
|
||||
{
|
||||
static bool set_regs_flag = false;
|
||||
|
||||
std::vector<std::string> list_devices()
|
||||
{
|
||||
log::warn("This device is not driven using device files!");
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
void set_regs_enable(bool enable) {
|
||||
log::warn("This operation is not supported!");
|
||||
}
|
||||
|
||||
err::Err Camera::show_colorbar(bool enable)
|
||||
{
|
||||
// only set variable now
|
||||
// should control camera to show colorbar
|
||||
_show_colorbar = enable;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
static void generate_colorbar(image::Image &img)
|
||||
{
|
||||
int width = img.width();
|
||||
int height = img.height();
|
||||
int step = width / 8;
|
||||
int color_step = 255 / 7;
|
||||
int color = 0;
|
||||
uint8_t colors[8][3] = {
|
||||
{255, 255, 255},
|
||||
{255, 0, 0},
|
||||
{255, 127, 0},
|
||||
{255, 255, 0},
|
||||
{0, 255, 0},
|
||||
{0, 0, 255},
|
||||
{143, 0, 255},
|
||||
{0, 0, 0},
|
||||
};
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
image::Color _color(colors[i][0], colors[i][1], colors[i][2], 0, image::FMT_RGB888);
|
||||
img.draw_rect(i * step, 0, step, height, _color, -1);
|
||||
color += color_step;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int dev;
|
||||
int i2c_addr;
|
||||
bool raw;
|
||||
double fps;
|
||||
int exptime_max; // unit:us
|
||||
int exptime_min;
|
||||
} camera_priv_t;
|
||||
|
||||
Camera::Camera(int width, int height, image::Format format, const char *device, double fps, int buff_num, bool open, bool raw)
|
||||
{
|
||||
err::Err e;
|
||||
err::check_bool_raise(_check_format(format), "Format not support");
|
||||
|
||||
_width = (width == -1) ? 640 : width;
|
||||
_height = (height == -1) ? 480 : height;
|
||||
_format = format;
|
||||
_buff_num = buff_num;
|
||||
_show_colorbar = false;
|
||||
_open_set_regs = set_regs_flag;
|
||||
_device = "";
|
||||
_last_read_us = time::ticks_us();
|
||||
_invert_flip = false;
|
||||
_invert_mirror = false;
|
||||
_is_opened = false;
|
||||
|
||||
camera_priv_t *priv = (camera_priv_t *)malloc(sizeof(camera_priv_t));
|
||||
err::check_null_raise(priv, "camera_priv_t malloc error");
|
||||
memset(priv, 0, sizeof(camera_priv_t));
|
||||
priv->dev = 0;
|
||||
priv->raw = raw;
|
||||
priv->fps = fps;
|
||||
_param = priv;
|
||||
|
||||
|
||||
if (format == image::Format::FMT_RGB888 && _width * _height * 3 > 640 * 640 * 3) {
|
||||
log::warn("Note that we do not recommend using large resolution RGB888 images, which can take up a lot of memory!\r\n");
|
||||
}
|
||||
|
||||
// config fps
|
||||
if (fps == -1 && _width <= 1280 && _height <= 720) {
|
||||
_fps = 60;
|
||||
} else if (fps == -1) {
|
||||
_fps = 30;
|
||||
} else {
|
||||
_fps = fps;
|
||||
}
|
||||
|
||||
if ((_width > 1280 || _height > 720) && _fps > 30) {
|
||||
log::warn("Current fps is too high, will be be updated to 30fps! Currently only supported up to 720p 60fps or 1440p 30fps.\r\n");
|
||||
_fps = 30;
|
||||
} else if (_width <= 1280 && _height <= 720 && _fps > 60 && _fps != 80) {
|
||||
log::warn("Currently only supports fixed 30,60 and 80fps in 720p configuration, current configuration will be updated to 80fps.\r\n");
|
||||
_fps = 80;
|
||||
} else if (_width <= 1280 && _height <= 720 && _fps < 80 && _fps > 30 && _fps != 60) {
|
||||
log::warn("Currently only supports fixed 30,60 and 80fps in 720p configuration, current configuration will be updated to 60fps.\r\n");
|
||||
_fps = 60;
|
||||
}
|
||||
|
||||
// open camera
|
||||
if (open) {
|
||||
e = this->open(_width, _height, _format, _fps, _buff_num);
|
||||
err::check_raise(e, "camera open failed");
|
||||
}
|
||||
}
|
||||
|
||||
Camera::~Camera()
|
||||
{
|
||||
if (this->is_opened()) {
|
||||
this->close();
|
||||
}
|
||||
|
||||
if (_param) {
|
||||
free(_param);
|
||||
}
|
||||
}
|
||||
|
||||
int Camera::get_ch_nums()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int Camera::get_channel()
|
||||
{
|
||||
return this->_ch;
|
||||
}
|
||||
|
||||
bool Camera::_check_format(image::Format format) {
|
||||
if (format == image::FMT_RGB888 || format == image::FMT_BGR888
|
||||
|| format == image::FMT_RGBA8888 || format == image::FMT_BGRA8888
|
||||
|| format == image::FMT_YVU420SP || format == image::FMT_GRAYSCALE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool _get_board_config_path(char *path, int path_size)
|
||||
{
|
||||
if (fs::exists("/boot/board")) {
|
||||
snprintf(path, path_size, "/boot/board");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int _get_mclk_id(void) {
|
||||
char path[64];
|
||||
int mclk_id = 0;
|
||||
|
||||
err::check_bool_raise(_get_board_config_path(path, sizeof(path)), "Can't find board config file");
|
||||
|
||||
std::string mclk_id_str;
|
||||
auto device_configs = sys::device_configs();
|
||||
auto it = device_configs.find("cam_mclk");
|
||||
if (it != device_configs.end()) {
|
||||
mclk_id_str = it->second;
|
||||
}
|
||||
if (!mclk_id_str.empty()) {
|
||||
mclk_id = atoi(mclk_id_str.c_str());
|
||||
} else {
|
||||
std::string board_id = sys::device_id();
|
||||
if (board_id == "maixcam_pro") {
|
||||
mclk_id = 0;
|
||||
} else {
|
||||
mclk_id = 1;
|
||||
}
|
||||
}
|
||||
return mclk_id;
|
||||
}
|
||||
|
||||
static std::vector<int> _get_lane_id_from_board_file() {
|
||||
std::vector<int> lane_id;
|
||||
auto device_configs = sys::device_configs();
|
||||
auto it = device_configs.find("lane_id");
|
||||
if (it != device_configs.end()) {
|
||||
auto lane_id_str = it->second;
|
||||
std::string item;
|
||||
std::stringstream ss(lane_id_str);
|
||||
while (std::getline(ss, item, ',')) {
|
||||
lane_id.push_back(std::stoi(item));
|
||||
}
|
||||
}
|
||||
|
||||
return lane_id;
|
||||
}
|
||||
|
||||
static std::vector<int> _get_pn_swap_from_board_file() {
|
||||
std::vector<int> pn_swap;
|
||||
auto device_configs = sys::device_configs();
|
||||
auto it = device_configs.find("pn_swap");
|
||||
if (it != device_configs.end()) {
|
||||
auto pn_swap_str = it->second;
|
||||
std::string item;
|
||||
std::stringstream ss(pn_swap_str);
|
||||
while (std::getline(ss, item, ',')) {
|
||||
pn_swap.push_back(std::stoi(item));
|
||||
}
|
||||
}
|
||||
|
||||
return pn_swap;
|
||||
}
|
||||
|
||||
static std::vector<bool> _get_cam_flip_mirror(void) {
|
||||
char path[64];
|
||||
bool flip = 0, mirror = 0;
|
||||
bool flip_is_found = false, mirror_is_found = false;
|
||||
|
||||
err::check_bool_raise(_get_board_config_path(path, sizeof(path)), "Can't find board config file");
|
||||
|
||||
std::string flip_str;
|
||||
std::string mirror_str;
|
||||
auto device_configs = sys::device_configs();
|
||||
auto it = device_configs.find("cam_flip");
|
||||
if (it != device_configs.end()) {
|
||||
flip_str = it->second;
|
||||
flip_is_found = true;
|
||||
}
|
||||
auto it2 = device_configs.find("cam_mirror");
|
||||
if (it2 != device_configs.end()) {
|
||||
mirror_str = it2->second;
|
||||
mirror_is_found = true;
|
||||
}
|
||||
|
||||
std::string board_id = sys::device_id();
|
||||
// log::info("cam flip=%s, cam mirror=%s", flip_str, mirror_str);
|
||||
if (flip_is_found && !flip_str.empty()) {
|
||||
flip = atoi(flip_str.c_str());
|
||||
} else {
|
||||
if (board_id == "maixcam_pro") {
|
||||
flip = true;
|
||||
} else {
|
||||
flip = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mirror_is_found && !mirror_str.empty()) {
|
||||
mirror = atoi(mirror_str.c_str());
|
||||
} else {
|
||||
std::string board_id = sys::device_id();
|
||||
if (board_id == "maixcam_pro") {
|
||||
mirror = true;
|
||||
} else {
|
||||
mirror = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {flip, mirror};
|
||||
}
|
||||
|
||||
static std::pair<bool, std::string> _get_sensor_name(void)
|
||||
{
|
||||
char name[30];
|
||||
peripheral::i2c::I2C i2c_obj(4, peripheral::i2c::Mode::MASTER);
|
||||
|
||||
std::vector<int> addr_list = i2c_obj.scan();
|
||||
for (size_t i = 0; i < addr_list.size(); i++) {
|
||||
// log::info("i2c4 addr: 0x%02x", addr_list[i]);
|
||||
switch (addr_list[i]) {
|
||||
case 0x29:
|
||||
// log::info("find gcore_gc4653, addr %#x", addr_list[i]);
|
||||
snprintf(name, sizeof(name), "gcore_gc4653");
|
||||
return {true, name};
|
||||
case 0x30:
|
||||
// log::info("find sms_sc035gs, addr %#x", addr_list[i]);
|
||||
snprintf(name, sizeof(name), "sms_sc035gs");
|
||||
return {true, name};
|
||||
case 0x2b:
|
||||
// log::info("find lt6911, addr %#x", addr_list[i]);
|
||||
snprintf(name, sizeof(name), "lt6911");
|
||||
return {true, name};
|
||||
case 0x36:
|
||||
// log::info("find ov_os04a10, addr %#x", addr_list[i]);
|
||||
snprintf(name, sizeof(name), "ov_os04a10");
|
||||
return {true, name};
|
||||
case 0x37:
|
||||
// log::info("find ov_os04a10, addr %#x", addr_list[i]);
|
||||
snprintf(name, sizeof(name), "gcore_gc02m1");
|
||||
return {true, name};
|
||||
case 0x48:// fall through
|
||||
case 0x3c:
|
||||
// log::info("find ov_ov2685, addr %#x", addr_list[i]);
|
||||
snprintf(name, sizeof(name), "ov_ov2685");
|
||||
return {true, name};
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// log::info("sensor address not found , use gcore_gc4653\n" );
|
||||
snprintf(name, sizeof(name), "gcore_gc4653");
|
||||
return {false, name};
|
||||
}
|
||||
|
||||
std::string get_device_name()
|
||||
{
|
||||
std::string device_name;
|
||||
std::pair<bool, std::string> res = _get_sensor_name();
|
||||
if (res.first == false) {
|
||||
device_name = "";
|
||||
return device_name;
|
||||
} else {
|
||||
device_name = res.second;
|
||||
}
|
||||
return device_name;
|
||||
}
|
||||
|
||||
|
||||
static int _is_module_in_use(const char *module_name) {
|
||||
FILE *fp;
|
||||
char buffer[256];
|
||||
|
||||
fp = fopen("/proc/modules", "r");
|
||||
if (fp == NULL) {
|
||||
perror("fopen");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
|
||||
char mod_name[256];
|
||||
int usage_count;
|
||||
|
||||
sscanf(buffer, "%255s %*s %d", mod_name, &usage_count);
|
||||
|
||||
if (strcmp(mod_name, module_name) == 0) {
|
||||
fclose(fp);
|
||||
return usage_count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err::Err Camera::open(int width, int height, image::Format format, double fps, int buff_num)
|
||||
{
|
||||
// int width_tmp = (width == -1) ? _width : width;
|
||||
// int height_tmp = (height == -1) ? _height : height;
|
||||
// image::Format format_tmp = (format == image::FMT_INVALID) ? _format : format;
|
||||
// double fps_tmp = (fps == -1) ? _fps : fps;
|
||||
// int buff_num_tmp =( buff_num == -1) ? _buff_num : buff_num;
|
||||
// camera_priv_t *priv = (camera_priv_t *)_param;
|
||||
|
||||
// check format
|
||||
// err::check_bool_raise(_check_format(format_tmp), "Format not support");
|
||||
|
||||
_is_opened = true;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
void Camera::close()
|
||||
{
|
||||
if (this->is_closed())
|
||||
return;
|
||||
}
|
||||
|
||||
camera::Camera *Camera::add_channel(int width, int height, image::Format format, double fps, int buff_num, bool open)
|
||||
{
|
||||
err::check_bool_raise(_check_format(format), "Format not support");
|
||||
|
||||
int width_tmp = (width == -1) ? _width : width;
|
||||
int height_tmp = (height == -1) ? _height : height;
|
||||
image::Format format_tmp = (format == image::Format::FMT_INVALID) ? _format : format;
|
||||
double fps_tmp = (fps == -1) ? _fps : fps;
|
||||
int buff_num_tmp = buff_num == -1 ? _buff_num : buff_num;
|
||||
|
||||
Camera *cam = new Camera(width_tmp, height_tmp, format_tmp, _device.c_str(), fps_tmp, buff_num_tmp, true);
|
||||
return cam;
|
||||
}
|
||||
|
||||
bool Camera::is_opened()
|
||||
{
|
||||
return _is_opened;
|
||||
}
|
||||
|
||||
image::Image *Camera::read(void *buff, size_t buff_size, bool block, int block_ms)
|
||||
{
|
||||
if (!this->is_opened()) {
|
||||
err::Err e = open(_width, _height, _format, _fps, _buff_num);
|
||||
err::check_raise(e, "open camera failed");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// static image::Format _get_raw_format_with_size(int w, int h, int total_size, BAYER_FORMAT_E bayer_format) {
|
||||
// image::Format format = image::FMT_INVALID;
|
||||
// int size = w * h;
|
||||
// if (total_size == size * 0.75) {
|
||||
// switch (bayer_format) {
|
||||
// case BAYER_FORMAT_BG:
|
||||
// format = image::FMT_BGGR6;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GB:
|
||||
// format = image::FMT_GBRG6;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GR:
|
||||
// format = image::FMT_GRBG6;
|
||||
// break;
|
||||
// case BAYER_FORMAT_RG:
|
||||
// format = image::FMT_RGGB6;
|
||||
// break;
|
||||
// default:
|
||||
// return image::FMT_INVALID;
|
||||
// }
|
||||
// } else if (total_size == size * 1) {
|
||||
// switch (bayer_format) {
|
||||
// case BAYER_FORMAT_BG:
|
||||
// format = image::FMT_BGGR8;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GB:
|
||||
// format = image::FMT_GBRG8;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GR:
|
||||
// format = image::FMT_GRBG8;
|
||||
// break;
|
||||
// case BAYER_FORMAT_RG:
|
||||
// format = image::FMT_RGGB8;
|
||||
// break;
|
||||
// default:
|
||||
// return image::FMT_INVALID;
|
||||
// }
|
||||
// } else if (total_size == size * 1.25) {
|
||||
// switch (bayer_format) {
|
||||
// case BAYER_FORMAT_BG:
|
||||
// format = image::FMT_BGGR10;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GB:
|
||||
// format = image::FMT_GBRG10;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GR:
|
||||
// format = image::FMT_GRBG10;
|
||||
// break;
|
||||
// case BAYER_FORMAT_RG:
|
||||
// format = image::FMT_RGGB10;
|
||||
// break;
|
||||
// default:
|
||||
// return image::FMT_INVALID;
|
||||
// }
|
||||
// } else if (total_size == size * 1.5) {
|
||||
// switch (bayer_format) {
|
||||
// case BAYER_FORMAT_BG:
|
||||
// format = image::FMT_BGGR12;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GB:
|
||||
// format = image::FMT_GBRG12;
|
||||
// break;
|
||||
// case BAYER_FORMAT_GR:
|
||||
// format = image::FMT_GRBG12;
|
||||
// break;
|
||||
// case BAYER_FORMAT_RG:
|
||||
// format = image::FMT_RGGB12;
|
||||
// break;
|
||||
// default:
|
||||
// return image::FMT_INVALID;
|
||||
// }
|
||||
// } else {
|
||||
// return image::FMT_INVALID;
|
||||
// }
|
||||
|
||||
// return format;
|
||||
// }
|
||||
|
||||
image::Image *Camera::read_raw() {
|
||||
if (!this->is_opened()) {
|
||||
err::Err e = open(_width, _height, _format, _fps, _buff_num);
|
||||
err::check_raise(e, "open camera failed");
|
||||
}
|
||||
|
||||
camera_priv_t *priv = (camera_priv_t *)this->_param;
|
||||
if (!priv->raw) {
|
||||
err::check_raise(err::ERR_NOT_READY, "you need to enable the raw parameter when constructing the Camera object.");
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Camera::clear_buff()
|
||||
{
|
||||
log::warn("This operation is not supported!");
|
||||
}
|
||||
|
||||
void Camera::skip_frames(int num)
|
||||
{
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
image::Image *img = this->read();
|
||||
delete img;
|
||||
}
|
||||
}
|
||||
|
||||
err::Err Camera::set_resolution(int width, int height)
|
||||
{
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
static void _config_extern_register_of_os04a10(int exptime_ms)
|
||||
{
|
||||
char cmd[128];
|
||||
if (exptime_ms >= 1 * 1000) {
|
||||
double scale = 1.587; // (0x0c68 - 0x0635) / 1000
|
||||
double exp_time_ms = exptime_ms;
|
||||
uint64_t reg_val = (uint16_t)((exp_time_ms - 1000) * scale) + 0x0635;
|
||||
reg_val = reg_val > 0xffff ? 0xffff : reg_val;
|
||||
snprintf(cmd, sizeof(cmd), "i2ctransfer -fy 4 w4@0x36 0x38 0x0c %#.2x %#.2x", (uint8_t)((reg_val >> 8) & 0xff), (uint8_t)(reg_val & 0xff));
|
||||
system(cmd);
|
||||
} else {
|
||||
uint64_t reg_val = 0x05cc;
|
||||
snprintf(cmd, sizeof(cmd), "i2ctransfer -fy 4 w4@0x36 0x38 0x0c %#.2x %#.2x", (uint8_t)((reg_val >> 8) & 0xff), (uint8_t)(reg_val & 0xff));
|
||||
system(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
err::Err Camera::set_fps(double fps) {
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
int Camera::exposure(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::gain(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::hmirror(int value) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::vflip(int value) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::luma(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::constrast(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::saturation(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::awb_mode(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Camera::set_awb(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
value = 1;
|
||||
} else if (value > 0) {
|
||||
value = 0;
|
||||
}
|
||||
|
||||
return this->awb_mode(value) == 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
int Camera::exp_mode(int value) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
err::Err Camera::set_windowing(std::vector<int> roi) {
|
||||
if (!this->is_opened()) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
std::vector<int> Camera::get_sensor_size() {
|
||||
camera_priv_t *priv = (camera_priv_t *)_param;
|
||||
|
||||
return {640, 480};
|
||||
}
|
||||
|
||||
err::Err Camera::write_reg(int addr, int data, int bit_width)
|
||||
{
|
||||
camera_priv_t *priv = (camera_priv_t *)_param;
|
||||
(void)bit_width;
|
||||
peripheral::i2c::I2C i2c_obj(4, peripheral::i2c::Mode::MASTER);
|
||||
uint8_t temp[1];
|
||||
temp[0] = (uint8_t)data;
|
||||
i2c_obj.writeto_mem(priv->i2c_addr, addr, temp, sizeof(temp), 16);
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
int Camera::read_reg(int addr, int bit_width)
|
||||
{
|
||||
camera_priv_t *priv = (camera_priv_t *)_param;
|
||||
(void)bit_width;
|
||||
peripheral::i2c::I2C i2c_obj(4, peripheral::i2c::Mode::MASTER);
|
||||
Bytes *data = i2c_obj.readfrom_mem(priv->i2c_addr, addr, 1, 16);
|
||||
int out = -1;log::info("addr:%#x", priv->i2c_addr);
|
||||
if (data) {
|
||||
if (data->size() > 0) {
|
||||
out = data->data[0];
|
||||
}
|
||||
delete data;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
276
components/vision/port/maixcam2/maix_display_maixcam2.hpp
Normal file
276
components/vision/port/maixcam2/maix_display_maixcam2.hpp
Normal file
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* @author lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "maix_display_base.hpp"
|
||||
#include "maix_thread.hpp"
|
||||
#include "maix_image.hpp"
|
||||
#include "maix_time.hpp"
|
||||
#include <unistd.h>
|
||||
#include "maix_pwm.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <sys/mman.h>
|
||||
#include "maix_fs.hpp"
|
||||
|
||||
using namespace maix::peripheral;
|
||||
|
||||
namespace maix::display
|
||||
{
|
||||
enum class PanelType {
|
||||
NORMAL,
|
||||
LT9611,
|
||||
UNKNOWN,
|
||||
};
|
||||
inline static PanelType __g_panel_type = PanelType::UNKNOWN;
|
||||
|
||||
__attribute__((unused)) static int _get_vo_max_size(int *width, int *height, int rotate)
|
||||
{
|
||||
int w = 0, h = 0;
|
||||
|
||||
if (rotate) {
|
||||
*width = h;
|
||||
*height = w;
|
||||
} else {
|
||||
*width = w;
|
||||
*height = h;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void _get_disp_configs(bool &flip, bool &mirror, float &max_backlight) {
|
||||
std::string flip_str;
|
||||
bool flip_is_found = false;
|
||||
|
||||
auto device_configs = sys::device_configs();
|
||||
auto it = device_configs.find("disp_flip");
|
||||
if (it != device_configs.end()) {
|
||||
flip_str = it->second;
|
||||
flip_is_found = true;
|
||||
}
|
||||
auto it2 = device_configs.find("disp_mirror");
|
||||
if (it2 != device_configs.end()) {
|
||||
if (it2->second.size() > 0)
|
||||
mirror = atoi(it2->second.c_str());
|
||||
}
|
||||
auto it3 = device_configs.find("disp_max_backlight");
|
||||
if (it3 != device_configs.end()) {
|
||||
if (it3->second.size() > 0)
|
||||
max_backlight = atof(it3->second.c_str());
|
||||
}
|
||||
|
||||
if (flip_is_found && flip_str.size() > 0) {
|
||||
flip = atoi(flip_str.c_str());
|
||||
} else {
|
||||
std::string board_id = sys::device_id();
|
||||
if (board_id == "maixcam_pro") {
|
||||
flip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// log::info("disp config flip: %d, mirror: %d, max_backlight: %.1f", flip, mirror, max_backlight);
|
||||
}
|
||||
|
||||
|
||||
class DisplayAx final : public DisplayBase
|
||||
{
|
||||
public:
|
||||
DisplayAx(const string &device, int width, int height, image::Format format)
|
||||
{
|
||||
err::check_bool_raise(!_get_vo_max_size(&_max_width, &_max_height, 1), "get vo max size failed");
|
||||
width = width <= 0 ? _max_width : width;
|
||||
height = height <= 0 ? _max_height : height;
|
||||
this->_width = width > _max_width ? _max_width : width;
|
||||
this->_height = height > _max_height ? _max_height : height;
|
||||
this->_format = format;
|
||||
this->_opened = false;
|
||||
this->_format = format;
|
||||
this->_invert_flip = false;
|
||||
this->_invert_mirror = false;
|
||||
this->_layer = 0; // layer 0 means vedio layer
|
||||
err::check_bool_raise(_format == image::FMT_RGB888
|
||||
|| _format == image::FMT_YVU420SP
|
||||
|| _format == image::FMT_BGRA8888, "Format not support");
|
||||
|
||||
int pwm_id = 10;
|
||||
_bl_pwm = new pwm::PWM(pwm_id, 100000, 20);
|
||||
}
|
||||
|
||||
DisplayAx(int layer, int width, int height, image::Format format)
|
||||
{
|
||||
err::check_bool_raise(!_get_vo_max_size(&_max_width, &_max_height, 1), "get vo max size failed");
|
||||
width = width <= 0 ? _max_width : width;
|
||||
height = height <= 0 ? _max_height : height;
|
||||
this->_width = width > _max_width ? _max_width : width;
|
||||
this->_height = height > _max_height ? _max_height : height;
|
||||
this->_format = format;
|
||||
this->_opened = false;
|
||||
this->_format = format;
|
||||
this->_invert_flip = false;
|
||||
this->_invert_mirror = false;
|
||||
this->_max_backlight = 50.0;
|
||||
this->_layer = layer; // layer 0 means vedio layer
|
||||
// layer 1 means osd layer
|
||||
err::check_bool_raise(_format == image::FMT_BGRA8888, "Format not support");
|
||||
|
||||
int pwm_id = 10;
|
||||
_bl_pwm = new pwm::PWM(pwm_id, 100000, 20);
|
||||
}
|
||||
|
||||
~DisplayAx()
|
||||
{
|
||||
if(_bl_pwm && this->_layer == 0) // _layer = 0, means video layer
|
||||
{
|
||||
delete _bl_pwm;
|
||||
}
|
||||
}
|
||||
|
||||
int width()
|
||||
{
|
||||
return this->_width;
|
||||
}
|
||||
|
||||
int height()
|
||||
{
|
||||
return this->_height;
|
||||
}
|
||||
|
||||
std::vector<int> size()
|
||||
{
|
||||
return {this->_width, this->_height};
|
||||
}
|
||||
|
||||
image::Format format()
|
||||
{
|
||||
return this->_format;
|
||||
}
|
||||
|
||||
err::Err open(int width, int height, image::Format format)
|
||||
{
|
||||
width = width > _max_width ? _max_width : width;
|
||||
height = height > _max_height ? _max_height : height;
|
||||
if(this->_opened)
|
||||
{
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
this->_opened = true;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err close()
|
||||
{
|
||||
if (!this->_opened)
|
||||
return err::ERR_NONE;
|
||||
|
||||
this->_opened = false;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
display::DisplayAx *add_channel(int width, int height, image::Format format)
|
||||
{
|
||||
int new_width = 0;
|
||||
int new_height = 0;
|
||||
image::Format new_format = format;
|
||||
if (width == -1) {
|
||||
new_width = this->_width;
|
||||
} else {
|
||||
new_width = width > this->_width ? this->_width : width;
|
||||
}
|
||||
if (height == -1) {
|
||||
new_height = this->_height;
|
||||
} else {
|
||||
new_height = height > this->_height ? this->_height : height;
|
||||
}
|
||||
|
||||
_format = new_format;
|
||||
DisplayAx *disp = new DisplayAx(1, new_width, new_height, new_format);
|
||||
return disp;
|
||||
}
|
||||
|
||||
bool is_opened()
|
||||
{
|
||||
return this->_opened;
|
||||
}
|
||||
|
||||
err::Err show(image::Image &img, image::Fit fit)
|
||||
{
|
||||
err::check_bool_raise((img.width() % 2 == 0 && img.height() % 2 == 0), "Image width and height must be a multiple of 2.");
|
||||
int format = img.format();
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
void set_backlight(float value)
|
||||
{
|
||||
_bl_pwm->duty(value * _max_backlight / 100.0);
|
||||
_bl_pwm->disable();
|
||||
if(value == 0)
|
||||
return;
|
||||
_bl_pwm->enable();
|
||||
}
|
||||
|
||||
float get_backlight()
|
||||
{
|
||||
return _bl_pwm->duty() / _max_backlight * 100;
|
||||
}
|
||||
|
||||
int get_ch_nums()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
err::Err set_hmirror(bool en) {
|
||||
en = _invert_mirror ? !en : en;
|
||||
|
||||
bool need_open = false;
|
||||
if (this->_opened) {
|
||||
this->close();
|
||||
need_open = true;
|
||||
}
|
||||
|
||||
if (need_open) {
|
||||
err::check_raise(this->open(this->_width, this->_height, this->_format), "Open failed");
|
||||
}
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err set_vflip(bool en) {
|
||||
en = _invert_flip ? !en : en;
|
||||
|
||||
bool need_open = false;
|
||||
if (this->_opened) {
|
||||
this->close();
|
||||
need_open = true;
|
||||
}
|
||||
|
||||
if (need_open) {
|
||||
err::check_raise(this->open(this->_width, this->_height, this->_format), "Open failed");
|
||||
}
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
private:
|
||||
int _width;
|
||||
int _height;
|
||||
int _max_width;
|
||||
int _max_height;
|
||||
image::Format _format;
|
||||
int _layer;
|
||||
int _ch;
|
||||
bool _opened;
|
||||
bool _invert_flip;
|
||||
bool _invert_mirror;
|
||||
float _max_backlight;
|
||||
pwm::PWM *_bl_pwm;
|
||||
};
|
||||
}
|
||||
620
components/vision/port/maixcam2/maix_jpg_stream.cpp
Normal file
620
components/vision/port/maixcam2/maix_jpg_stream.cpp
Normal file
@@ -0,0 +1,620 @@
|
||||
/**
|
||||
* @author lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2024.5.17: Add framework, create this file.
|
||||
*/
|
||||
|
||||
#include "maix_jpg_stream.hpp"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/socket.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define BOUNDARY "frame"
|
||||
|
||||
static const char *default_index_str =
|
||||
"<html>\n"
|
||||
"<body>\n"
|
||||
"<h1>JPG Stream</h1>\n"
|
||||
"<img src='/stream'>\n"
|
||||
"</body>\n"
|
||||
"</html>";
|
||||
|
||||
typedef struct {
|
||||
int socket;
|
||||
int thread;
|
||||
int idx;
|
||||
uint8_t is_inited;
|
||||
uint8_t is_running;
|
||||
uint8_t try_update_buffer;
|
||||
uint8_t try_exit;
|
||||
pthread_mutex_t lock;
|
||||
} client_info_t;
|
||||
|
||||
typedef struct {
|
||||
void *p;
|
||||
size_t size;
|
||||
uint8_t valid;
|
||||
} buffer_info_t;
|
||||
|
||||
typedef struct {
|
||||
int socket_fd;
|
||||
struct sockaddr_in address;
|
||||
int addrlen;
|
||||
|
||||
pthread_mutex_t lock;
|
||||
int thread;
|
||||
uint8_t thread_is_started;
|
||||
uint8_t try_exit_thread;
|
||||
|
||||
int client_cnt;
|
||||
int client_max;
|
||||
client_info_t *client;
|
||||
|
||||
buffer_info_t buffer[2];
|
||||
int new_buffer_idx;
|
||||
|
||||
char *index_str;
|
||||
} priv_t;
|
||||
|
||||
priv_t priv;
|
||||
|
||||
|
||||
static char *get_ip_str(char *hostname) {
|
||||
struct addrinfo hints, *res, *p;
|
||||
int status;
|
||||
char *ipstr = (char *)malloc(INET6_ADDRSTRLEN);
|
||||
if (!ipstr) return NULL;
|
||||
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (p = res; p != NULL; p = p->ai_next) {
|
||||
void *addr;
|
||||
if (p->ai_family == AF_INET) {
|
||||
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
||||
addr = &(ipv4->sin_addr);
|
||||
} else { // IPv6
|
||||
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
addr = &(ipv6->sin6_addr);
|
||||
}
|
||||
inet_ntop(p->ai_family, addr, ipstr, INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return ipstr;
|
||||
}
|
||||
|
||||
static int socket_is_connected(int sockfd) {
|
||||
char buffer;
|
||||
int result = recv(sockfd, &buffer, 1, MSG_PEEK | MSG_DONTWAIT);
|
||||
if (result == 0) {
|
||||
return 0;
|
||||
} else if (result < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t socket_write(int socket, void *data, size_t size)
|
||||
{
|
||||
if (!socket_is_connected(socket)) {
|
||||
return -1;
|
||||
}
|
||||
return write(socket, data, size);
|
||||
}
|
||||
|
||||
static size_t socket_read(int socket, void *data, size_t size)
|
||||
{
|
||||
return read(socket, data, size);
|
||||
}
|
||||
|
||||
void send_response(int client_socket, char *header, char *body) {
|
||||
socket_write(client_socket, header, strlen(header));
|
||||
socket_write(client_socket, body, strlen(body));
|
||||
}
|
||||
|
||||
static int find_unused_idx(client_info_t *client, int client_max)
|
||||
{
|
||||
for (int i = 0; i < client_max; i ++) {
|
||||
client_info_t *c = (client_info_t *)&client[i];
|
||||
if (!c->is_inited) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int notify_client_update_buffer()
|
||||
{
|
||||
for (int i = 0; i < priv.client_max; i ++) {
|
||||
client_info_t *c = (client_info_t *)&priv.client[i];
|
||||
if (c->is_inited) {
|
||||
pthread_mutex_lock(&c->lock);
|
||||
c->try_update_buffer = 1;
|
||||
pthread_mutex_unlock(&c->lock);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_global_buffer_not_safe(void **buffer, size_t *size)
|
||||
{
|
||||
buffer_info_t *info = (buffer_info_t *)&priv.buffer[priv.new_buffer_idx];
|
||||
if (info->valid) {
|
||||
if (!info->p) return -1;
|
||||
|
||||
if (buffer) *buffer = info->p;
|
||||
if (size) *size = info->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void on_stream(client_info_t *client) {
|
||||
int client_socket = client->socket;
|
||||
char header[1024];
|
||||
sprintf(header, "HTTP/1.1 200 OK\r\n");
|
||||
sprintf(header + strlen(header), "Content-Type: multipart/x-mixed-replace; boundary=%s\r\n", BOUNDARY);
|
||||
sprintf(header + strlen(header), "\r\n");
|
||||
socket_write(client_socket, header, strlen(header));
|
||||
|
||||
int update = 0;
|
||||
while (1) {
|
||||
pthread_mutex_lock(&client->lock);
|
||||
if (client->try_exit || !socket_is_connected(client->socket)) {
|
||||
pthread_mutex_unlock(&client->lock);
|
||||
break;
|
||||
}
|
||||
|
||||
if (client->try_update_buffer) {
|
||||
update = 1;
|
||||
client->try_update_buffer = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&client->lock);
|
||||
|
||||
size_t image_size;
|
||||
char *image_data;
|
||||
if (update) {
|
||||
update = 0;
|
||||
|
||||
pthread_mutex_lock(&priv.lock);
|
||||
void *buffer;
|
||||
size_t buffer_size;
|
||||
if (0 != get_global_buffer_not_safe(&buffer, &buffer_size)) {
|
||||
printf("get global buffer failed!\r\n");
|
||||
usleep(100*1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
image_size = buffer_size;
|
||||
image_data = (char *)malloc(image_size);
|
||||
if (!image_data) {
|
||||
printf("malloc failed!\r\n");
|
||||
usleep(100*1000);
|
||||
continue;
|
||||
}
|
||||
memcpy(image_data, buffer, image_size);
|
||||
pthread_mutex_unlock(&priv.lock);
|
||||
|
||||
sprintf(header, "--%s\r\n", BOUNDARY);
|
||||
sprintf(header + strlen(header), "Content-Type: image/jpeg\r\n");
|
||||
sprintf(header + strlen(header), "Content-Length: %ld\r\n", image_size);
|
||||
sprintf(header + strlen(header), "\r\n");
|
||||
socket_write(client_socket, header, strlen(header));
|
||||
socket_write(client_socket, image_data, image_size);
|
||||
socket_write(client_socket, (char *)"\r\n", 2);
|
||||
free(image_data);
|
||||
} else {
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int http_jpeg_server_create(char *host, int port, int client_num) {
|
||||
int server_fd;
|
||||
struct sockaddr_in address;
|
||||
int opt = 1;
|
||||
int addrlen = sizeof(address);
|
||||
|
||||
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
|
||||
perror("socket failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
|
||||
perror("setsockopt");
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
if (host == 0 || strlen(host) == 0) {
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
} else {
|
||||
char *ip = (char *)get_ip_str(host);
|
||||
if (!ip) {
|
||||
printf("can not parse ip:%s\r\n", host);
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
address.sin_addr.s_addr = inet_addr(ip);
|
||||
free(ip);
|
||||
}
|
||||
address.sin_port = htons(port);
|
||||
|
||||
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
|
||||
perror("bind failed");
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(server_fd, 3) < 0) {
|
||||
perror("listen");
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != pthread_mutex_init(&priv.lock, NULL)) {
|
||||
printf("create lock failed!\r\n");
|
||||
close(server_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
priv.socket_fd = server_fd;
|
||||
memcpy(&priv.address, &address, sizeof(struct sockaddr_in));
|
||||
priv.addrlen = addrlen;
|
||||
priv.client_max = client_num;
|
||||
priv.client = (client_info_t *)malloc(priv.client_max * sizeof(client_info_t));
|
||||
if (priv.client == NULL) {
|
||||
printf("create client info failed!\r\n");
|
||||
return -1;
|
||||
}
|
||||
memset(priv.client, 0, priv.client_max * sizeof(client_info_t));
|
||||
memset(priv.buffer, 0, sizeof(priv.buffer));
|
||||
priv.client_cnt = 0;
|
||||
priv.index_str = (char *)default_index_str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *client_thread_handle(void *param)
|
||||
{
|
||||
int res;
|
||||
pthread_mutex_lock(&priv.lock);
|
||||
client_info_t *client = (client_info_t *)param;
|
||||
pthread_mutex_t *lock = &client->lock;
|
||||
pthread_mutex_unlock(&priv.lock);
|
||||
|
||||
pthread_mutex_lock(lock);
|
||||
int client_socket = client->socket;
|
||||
client->is_running = 1;
|
||||
pthread_mutex_unlock(lock);
|
||||
|
||||
fd_set readfds;
|
||||
while (1) {
|
||||
pthread_mutex_lock(lock);
|
||||
if (client->try_exit || !socket_is_connected(client->socket)) {
|
||||
pthread_mutex_unlock(lock);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(lock);
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(client_socket, &readfds);
|
||||
|
||||
char buffer[1024] = {0};
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(client_socket, &fds);
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100 * 1000;
|
||||
int ret = select(client_socket + 1, &fds, NULL, NULL, &tv);
|
||||
if (ret == -1) {
|
||||
printf("select error! ret:%d\r\n", ret);
|
||||
continue;
|
||||
} else if (ret == 0) {
|
||||
continue; // timeout
|
||||
} else {
|
||||
res = socket_read(client_socket, buffer, 1024);
|
||||
if (res < 0) {
|
||||
printf("socket_read failed! res: %d\r\n", res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (strstr(buffer, "GET /stream") != NULL) {
|
||||
on_stream(client);
|
||||
} else {
|
||||
char *response = priv.index_str;
|
||||
socket_write(client_socket, (char *)"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n", strlen("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"));
|
||||
socket_write(client_socket, response, strlen(response));
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&priv.lock);
|
||||
if (client->is_inited) {
|
||||
close(client->socket);
|
||||
client->socket = -1;
|
||||
client->thread = -1; // will exit
|
||||
client->is_running = 0;
|
||||
client->is_inited = 0;
|
||||
priv.client_cnt --;
|
||||
pthread_mutex_destroy(&client->lock);
|
||||
}
|
||||
pthread_mutex_unlock(&priv.lock);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void *thread_handle(void *param)
|
||||
{
|
||||
int res;
|
||||
priv_t *priv = (priv_t *)param;
|
||||
int server_fd;
|
||||
|
||||
pthread_mutex_lock(&priv->lock);
|
||||
server_fd = priv->socket_fd;
|
||||
pthread_mutex_unlock(&priv->lock);
|
||||
while (1) {
|
||||
pthread_mutex_lock(&priv->lock);
|
||||
if (priv->try_exit_thread) {
|
||||
pthread_mutex_unlock(&priv->lock);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&priv->lock);
|
||||
|
||||
int client_socket;
|
||||
if ((client_socket = accept(server_fd, (struct sockaddr *)&priv->address, (socklen_t*)&priv->addrlen)) < 0) {
|
||||
perror("accept");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&priv->lock);
|
||||
int idx = find_unused_idx(priv->client, priv->client_max);
|
||||
if (idx < 0) {
|
||||
printf("can not create more client! curr:%d max:%d\r\n", priv->client_cnt, priv->client_max);
|
||||
continue;;
|
||||
}
|
||||
|
||||
client_info_t *client = (client_info_t *)&priv->client[idx];
|
||||
client->socket = client_socket;
|
||||
client->try_exit = 0;
|
||||
client->idx = idx;
|
||||
if (0 != pthread_mutex_init(&client->lock, NULL)) {
|
||||
printf("create client lock failed!\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (0 != (res = pthread_create((pthread_t *)&client->thread, NULL, client_thread_handle, client))) {
|
||||
fprintf(stderr, "create client thread error:%s\n", strerror(res));
|
||||
pthread_mutex_destroy(&client->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if (0 != (res = pthread_detach(&client->thread))) {
|
||||
// fprintf(stderr, "client thread detach error:%s\n", strerror(res));
|
||||
// pthread_mutex_destroy(&client->lock);
|
||||
// continue;
|
||||
// }
|
||||
client->is_inited = 1;
|
||||
priv->client_cnt ++;
|
||||
pthread_mutex_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int http_jpeg_server_set_index_str(char *index)
|
||||
{
|
||||
if (priv.index_str != default_index_str) {
|
||||
if (priv.index_str) {
|
||||
free(priv.index_str);
|
||||
priv.index_str = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
priv.index_str = (char *)malloc(strlen(index) + 1);
|
||||
if (!priv.index_str) {
|
||||
printf("malloc failed!\r\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(priv.index_str, index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_jpeg_server_start() {
|
||||
pthread_t thread;
|
||||
|
||||
pthread_mutex_lock(&priv.lock);
|
||||
if (priv.thread_is_started) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv.try_exit_thread = 0;
|
||||
if (0 != pthread_create(&thread, NULL, thread_handle, &priv)) {
|
||||
printf("create thread failed!\r\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
priv.thread = thread;
|
||||
priv.thread_is_started = 1;
|
||||
pthread_mutex_unlock(&priv.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_jpeg_server_stop() {
|
||||
pthread_mutex_lock(&priv.lock);
|
||||
if (!priv.thread_is_started) {
|
||||
return 0;
|
||||
}
|
||||
priv.try_exit_thread = 1;
|
||||
pthread_mutex_unlock(&priv.lock);
|
||||
|
||||
for (int i = 0; i < priv.client_max; i ++) {
|
||||
client_info_t *c = (client_info_t *)&priv.client[i];
|
||||
if (c->is_inited) {
|
||||
pthread_mutex_lock(&c->lock);
|
||||
c->try_exit = 1;
|
||||
pthread_mutex_unlock(&c->lock);
|
||||
}
|
||||
}
|
||||
|
||||
// pthread_join(priv.thread, NULL);
|
||||
priv.thread_is_started = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int http_jpeg_server_destory() {
|
||||
http_jpeg_server_stop();
|
||||
|
||||
if (priv.client) {
|
||||
free(priv.client);
|
||||
priv.client = NULL;
|
||||
}
|
||||
|
||||
if (priv.socket_fd > 0) {
|
||||
close(priv.socket_fd);
|
||||
priv.socket_fd = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&priv.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_jpeg_server_send(void *data, size_t size)
|
||||
{
|
||||
pthread_mutex_lock(&priv.lock);
|
||||
int next_buffer_idx = !priv.new_buffer_idx;
|
||||
if (priv.buffer[next_buffer_idx].p) {
|
||||
free(priv.buffer[next_buffer_idx].p);
|
||||
priv.buffer[next_buffer_idx].p = NULL;
|
||||
}
|
||||
priv.buffer[next_buffer_idx].p = malloc(size);
|
||||
if (priv.buffer[next_buffer_idx].p == NULL) {
|
||||
printf("create new buffer failed!\r\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(priv.buffer[next_buffer_idx].p, data, size);
|
||||
priv.buffer[next_buffer_idx].size = size;
|
||||
priv.buffer[next_buffer_idx].valid = 1;
|
||||
priv.new_buffer_idx = next_buffer_idx;
|
||||
pthread_mutex_unlock(&priv.lock);
|
||||
|
||||
notify_client_update_buffer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace maix::http
|
||||
{
|
||||
JpegStreamer::JpegStreamer(std::string host, int port, int client_number) {
|
||||
int res = 0;
|
||||
if (host.size() == 0) {
|
||||
host = "0.0.0.0";
|
||||
}
|
||||
|
||||
if (0 != (res = http_jpeg_server_create((char *)host.c_str(), port, client_number))) {
|
||||
err::check_raise(err::ERR_RUNTIME, "http_jpeg_server_create failed!");
|
||||
}
|
||||
|
||||
_host = host;
|
||||
_port = port;
|
||||
}
|
||||
|
||||
JpegStreamer::~JpegStreamer() {
|
||||
int res = 0;
|
||||
if (0 != (res = http_jpeg_server_destory())) {
|
||||
err::check_raise(err::ERR_RUNTIME, "http_jpeg_server_start failed!");
|
||||
}
|
||||
}
|
||||
|
||||
err::Err JpegStreamer::start() {
|
||||
int res = 0;
|
||||
if (0 != (res = http_jpeg_server_start())) {
|
||||
log::error("http_jpeg_server_start failed! res:%d\r\n", res);
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err JpegStreamer::stop() {
|
||||
int res = 0;
|
||||
if (0 != (res = http_jpeg_server_stop())) {
|
||||
log::error("http_jpeg_server_stop failed! res:%d\r\n", res);
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err JpegStreamer::write(image::Image *img) {
|
||||
int res = 0;
|
||||
image::Image *jpg = NULL;
|
||||
|
||||
if (img->format() != image::Format::FMT_JPEG) {
|
||||
jpg = img->to_jpeg();
|
||||
if (jpg == NULL) {
|
||||
log::error("invert to jpeg failed!\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
} else {
|
||||
jpg = img;
|
||||
}
|
||||
|
||||
if (0 != (res = http_jpeg_server_send(jpg->data(), jpg->data_size()))) {
|
||||
log::error("http_jpeg_server_send failed! res:%d\r\n", res);
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
if (img->format() != image::Format::FMT_JPEG) {
|
||||
delete jpg;
|
||||
}
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err JpegStreamer::set_html(std::string data) {
|
||||
int res = 0;
|
||||
if (data.size() == 0) {
|
||||
log::error("html code is none!\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
if (0 != (res = http_jpeg_server_set_index_str(&data[0]))) {
|
||||
log::error("http_jpeg_server_set_index_str failed! res:%d\r\n", res);
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
}
|
||||
791
components/vision/port/maixcam2/maix_rtmp_maixcam2.cpp
Normal file
791
components/vision/port/maixcam2/maix_rtmp_maixcam2.cpp
Normal file
@@ -0,0 +1,791 @@
|
||||
#include "maix_rtmp.hpp"
|
||||
|
||||
#include "maix_video.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
#include <stdio.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <list>
|
||||
|
||||
extern "C" {
|
||||
#include <unistd.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavutil/avutil.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswresample/swresample.h>
|
||||
}
|
||||
|
||||
using namespace maix;
|
||||
|
||||
class RTMPClient
|
||||
{
|
||||
typedef struct {
|
||||
std::string url;
|
||||
AVFormatContext *format_context = nullptr;
|
||||
bool has_video = true;
|
||||
AVStream *video_stream = nullptr;
|
||||
enum AVCodecID video_codec_id;
|
||||
int video_width;
|
||||
int video_height;
|
||||
enum AVPixelFormat vidoe_pixel_format;
|
||||
uint32_t video_bitrate;
|
||||
AVRational video_timebase;
|
||||
uint8_t *video_sps_pps = nullptr;
|
||||
size_t video_sps_pps_size;
|
||||
size_t video_frame_count = 0;
|
||||
size_t video_last_pts = 0;
|
||||
|
||||
bool has_audio = false;
|
||||
AVStream *audio_stream = nullptr;
|
||||
AVCodec *audio_codec = nullptr;
|
||||
AVFrame *audio_frame = nullptr;
|
||||
AVCodecContext *audio_codec_ctx = NULL;
|
||||
SwrContext *audio_swr_ctx = nullptr;
|
||||
std::list<std::pair<size_t, Bytes *>> *pcm_list;
|
||||
int audio_sample_rate;
|
||||
int audio_channels;
|
||||
int audio_bitrate;
|
||||
enum AVSampleFormat audio_format;
|
||||
} ffmpeg_param_t;
|
||||
|
||||
ffmpeg_param_t _ffmpeg;
|
||||
bool _open;
|
||||
public:
|
||||
static int ffmpeg_init(ffmpeg_param_t *ffmpeg)
|
||||
{
|
||||
avformat_network_init();
|
||||
AVFormatContext *format_context = NULL;
|
||||
avformat_alloc_output_context2(&format_context, NULL, "flv", ffmpeg->url.c_str());
|
||||
if (!format_context) {
|
||||
log::info("Can't create output context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ffmpeg->has_video) {
|
||||
AVStream *video_stream = avformat_new_stream(format_context, NULL);
|
||||
if (!video_stream) {
|
||||
log::info("Can't create video stream");
|
||||
goto _free_format_context;
|
||||
}
|
||||
video_stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
video_stream->codecpar->codec_id = ffmpeg->video_codec_id;
|
||||
video_stream->codecpar->width = ffmpeg->video_width;
|
||||
video_stream->codecpar->height = ffmpeg->video_height;
|
||||
video_stream->codecpar->format = ffmpeg->vidoe_pixel_format;
|
||||
video_stream->codecpar->bit_rate = ffmpeg->video_bitrate;
|
||||
video_stream->codecpar->codec_tag = 0;
|
||||
video_stream->codecpar->extradata = ffmpeg->video_sps_pps;
|
||||
video_stream->codecpar->extradata_size = ffmpeg->video_sps_pps_size;
|
||||
video_stream->time_base = ffmpeg->video_timebase;
|
||||
if (video_stream->codecpar->extradata == NULL ||
|
||||
video_stream->codecpar->extradata_size == 0) {
|
||||
log::info("video sps pps is null, size is 0");
|
||||
goto _free_format_context;
|
||||
}
|
||||
|
||||
ffmpeg->video_stream = video_stream;
|
||||
}
|
||||
|
||||
if (ffmpeg->has_audio) {
|
||||
int sample_rate = 48000;
|
||||
int channels = 1;
|
||||
int bitrate = 128000;
|
||||
enum AVSampleFormat format = AV_SAMPLE_FMT_S16;
|
||||
|
||||
AVStream *audio_stream = avformat_new_stream(format_context, NULL);
|
||||
if (!audio_stream) {
|
||||
log::info("Can't create audio stream");
|
||||
goto _free_format_context;
|
||||
}
|
||||
|
||||
AVCodec *audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
|
||||
if (!audio_codec) {
|
||||
log::info("Can't find audio encoder");
|
||||
goto _free_format_context;
|
||||
}
|
||||
|
||||
AVCodecContext *audio_codec_ctx = avcodec_alloc_context3(audio_codec);
|
||||
if (!audio_codec_ctx) {
|
||||
log::info("Can't alloc audio codec context");
|
||||
goto _free_format_context;
|
||||
}
|
||||
audio_codec_ctx->codec_id = AV_CODEC_ID_AAC;
|
||||
audio_codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||
audio_codec_ctx->sample_rate = sample_rate;
|
||||
audio_codec_ctx->channels = channels;
|
||||
audio_codec_ctx->channel_layout = av_get_default_channel_layout(audio_codec_ctx->channels);
|
||||
audio_codec_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;
|
||||
audio_codec_ctx->time_base = (AVRational){1, sample_rate};
|
||||
audio_codec_ctx->bit_rate = bitrate;
|
||||
audio_stream->time_base = audio_codec_ctx->time_base;
|
||||
|
||||
if (0 > avcodec_open2(audio_codec_ctx, audio_codec, NULL)) {
|
||||
log::info("Can't open audio codec");
|
||||
goto _free_audio_codec_ctx;
|
||||
}
|
||||
|
||||
if (0 > avcodec_parameters_from_context(audio_stream->codecpar, audio_codec_ctx)) {
|
||||
log::info("Can't copy audio codec parameters");
|
||||
goto _free_audio_codec_ctx;
|
||||
}
|
||||
|
||||
SwrContext *swr_ctx = swr_alloc();
|
||||
if (!swr_ctx) {
|
||||
log::info("Can't alloc swr context");
|
||||
goto _free_audio_codec_ctx;
|
||||
}
|
||||
|
||||
av_opt_set_int(swr_ctx, "in_channel_layout", audio_codec_ctx->channel_layout, 0);
|
||||
av_opt_set_int(swr_ctx, "out_channel_layout", audio_codec_ctx->channel_layout, 0);
|
||||
av_opt_set_int(swr_ctx, "in_sample_rate", audio_codec_ctx->sample_rate, 0);
|
||||
av_opt_set_int(swr_ctx, "out_sample_rate", audio_codec_ctx->sample_rate, 0);
|
||||
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", format, 0);
|
||||
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
|
||||
swr_init(swr_ctx);
|
||||
|
||||
AVFrame *audio_frame = av_frame_alloc();
|
||||
if (!audio_frame) {
|
||||
log::info("Can't alloc audio frame");
|
||||
swr_free(&swr_ctx);
|
||||
swr_ctx = NULL;
|
||||
goto _free_swr_ctx;
|
||||
}
|
||||
audio_frame->nb_samples = audio_codec_ctx->frame_size;
|
||||
audio_frame->channel_layout = audio_codec_ctx->channel_layout;
|
||||
audio_frame->format = AV_SAMPLE_FMT_FLTP;
|
||||
audio_frame->sample_rate = audio_codec_ctx->sample_rate;
|
||||
av_frame_get_buffer(audio_frame, 0);
|
||||
|
||||
ffmpeg->audio_stream = audio_stream;
|
||||
ffmpeg->audio_codec = audio_codec;
|
||||
ffmpeg->audio_codec_ctx = audio_codec_ctx;
|
||||
ffmpeg->audio_swr_ctx = swr_ctx;
|
||||
ffmpeg->audio_frame = audio_frame;
|
||||
ffmpeg->audio_sample_rate = sample_rate;
|
||||
ffmpeg->audio_channels = channels;
|
||||
ffmpeg->audio_bitrate = bitrate;
|
||||
ffmpeg->audio_format = format;
|
||||
}
|
||||
|
||||
if (avio_open(&format_context->pb, ffmpeg->url.c_str(), AVIO_FLAG_WRITE) < 0) {
|
||||
log::info("rtmp connect failed!");
|
||||
goto _free_audio_frame;
|
||||
}
|
||||
|
||||
if (avformat_write_header(format_context, NULL) < 0) {
|
||||
log::info("rtmp write header failed!");
|
||||
goto _close_io;
|
||||
}
|
||||
|
||||
ffmpeg->format_context = format_context;
|
||||
return 0;
|
||||
_close_io:
|
||||
if (ffmpeg->format_context && ffmpeg->format_context->pb) {
|
||||
avio_closep(&ffmpeg->format_context->pb);
|
||||
ffmpeg->format_context->pb = nullptr;
|
||||
}
|
||||
_free_audio_frame:
|
||||
if (ffmpeg->audio_frame) {
|
||||
av_frame_free(&ffmpeg->audio_frame);
|
||||
ffmpeg->audio_frame = nullptr;
|
||||
}
|
||||
_free_swr_ctx:
|
||||
if (ffmpeg->audio_swr_ctx) {
|
||||
swr_free(&ffmpeg->audio_swr_ctx);
|
||||
ffmpeg->audio_swr_ctx = nullptr;
|
||||
}
|
||||
_free_audio_codec_ctx:
|
||||
if (ffmpeg->audio_codec_ctx) {
|
||||
avcodec_free_context(&ffmpeg->audio_codec_ctx);
|
||||
ffmpeg->audio_codec_ctx = nullptr;
|
||||
}
|
||||
_free_format_context:
|
||||
if (ffmpeg->format_context) {
|
||||
avformat_free_context(ffmpeg->format_context);
|
||||
ffmpeg->format_context = nullptr;
|
||||
}
|
||||
|
||||
avformat_network_deinit();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ffmpeg_deinit(ffmpeg_param_t *ffmpeg)
|
||||
{
|
||||
if (!ffmpeg) return 0;
|
||||
|
||||
/* free sps pps in avformat_free_context */
|
||||
/*
|
||||
if (ffmpeg->video_sps_pps) {
|
||||
free(ffmpeg->video_sps_pps);
|
||||
ffmpeg->video_sps_pps = NULL;
|
||||
ffmpeg->video_sps_pps_size = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
if (ffmpeg->audio_frame) {
|
||||
av_frame_free(&ffmpeg->audio_frame);
|
||||
ffmpeg->audio_frame = nullptr;
|
||||
}
|
||||
|
||||
if (ffmpeg->audio_swr_ctx) {
|
||||
swr_free(&ffmpeg->audio_swr_ctx);
|
||||
ffmpeg->audio_swr_ctx = nullptr;
|
||||
}
|
||||
|
||||
if (ffmpeg->audio_codec_ctx) {
|
||||
avcodec_free_context(&ffmpeg->audio_codec_ctx);
|
||||
ffmpeg->audio_codec_ctx = nullptr;
|
||||
}
|
||||
|
||||
if (ffmpeg->format_context) {
|
||||
// av_write_trailer(ffmpeg->format_context);
|
||||
if (ffmpeg->format_context && ffmpeg->format_context->pb) {
|
||||
avio_closep(&ffmpeg->format_context->pb);
|
||||
}
|
||||
avformat_free_context(ffmpeg->format_context);
|
||||
ffmpeg->format_context = NULL;
|
||||
}
|
||||
|
||||
avformat_network_deinit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
RTMPClient() {
|
||||
_ffmpeg.has_video = true;
|
||||
_ffmpeg.pcm_list = new std::list<std::pair<size_t, Bytes *>>;
|
||||
_ffmpeg.video_last_pts = 0;
|
||||
err::check_null_raise(_ffmpeg.pcm_list, "create pcm list failed!");
|
||||
_open = false;
|
||||
}
|
||||
|
||||
~RTMPClient() {
|
||||
if (_ffmpeg.pcm_list) {
|
||||
for (auto it = _ffmpeg.pcm_list->begin(); it != _ffmpeg.pcm_list->end(); ++it) {
|
||||
auto &item = *it;
|
||||
Bytes *pcm = item.second;
|
||||
delete pcm;
|
||||
it = _ffmpeg.pcm_list->erase(it);
|
||||
}
|
||||
delete _ffmpeg.pcm_list;
|
||||
_ffmpeg.pcm_list = nullptr;
|
||||
}
|
||||
|
||||
if (_open) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
err::Err open()
|
||||
{
|
||||
if (_open) return err::ERR_NONE;
|
||||
|
||||
if (ffmpeg_init(&_ffmpeg) < 0) {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
_open = true;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err close()
|
||||
{
|
||||
if (!_open) return err::ERR_NONE;
|
||||
|
||||
if (ffmpeg_deinit(&_ffmpeg) < 0) {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
_open = false;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err config(std::string cmd, int data)
|
||||
{
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
|
||||
if (cmd == "has_video") {
|
||||
ffmpeg->has_video = data ? true : false;
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "video_codec_id") {
|
||||
ffmpeg->video_codec_id = (AVCodecID)data;
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "video_width") {
|
||||
ffmpeg->video_width = data;
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "video_height") {
|
||||
ffmpeg->video_height = data;
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "video_bitrate") {
|
||||
ffmpeg->video_bitrate = data;
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "video_fps") {
|
||||
ffmpeg->video_timebase = (AVRational){1, data};
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "video_pixel_format") {
|
||||
ffmpeg->vidoe_pixel_format = (AVPixelFormat)data;
|
||||
return err::ERR_NONE;
|
||||
} else if (cmd == "has_audio") {
|
||||
ffmpeg->has_audio = data ? true : false;
|
||||
return err::ERR_NONE;
|
||||
} else {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
err::Err config_url(std::string url) {
|
||||
_ffmpeg.url = url;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err config_sps_pps(uint8_t *sps_pps, int sps_pps_size)
|
||||
{
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
|
||||
if (ffmpeg->video_sps_pps) {
|
||||
free(ffmpeg->video_sps_pps);
|
||||
ffmpeg->video_sps_pps = NULL;
|
||||
}
|
||||
ffmpeg->video_sps_pps = (uint8_t *)malloc(sps_pps_size);
|
||||
if (!ffmpeg->video_sps_pps) {
|
||||
return err::ERR_NO_MEM;
|
||||
}
|
||||
memcpy(ffmpeg->video_sps_pps, sps_pps, sps_pps_size);
|
||||
ffmpeg->video_sps_pps_size = sps_pps_size;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err push(uint8_t *frame, size_t frame_size, uint64_t pts, bool is_audio = false)
|
||||
{
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
if (!_open) {
|
||||
return err::ERR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!is_audio) {
|
||||
if (ffmpeg->has_video) {
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
if (!pkt) {
|
||||
fprintf(stderr, "Can't malloc avpacket\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
pkt->data = frame;
|
||||
pkt->size = frame_size;
|
||||
pkt->stream_index = ffmpeg->video_stream->index;
|
||||
pkt->duration = pts - _ffmpeg.video_last_pts;
|
||||
_ffmpeg.video_last_pts = pts;
|
||||
pkt->pts = pkt->dts = pts;
|
||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||
ffmpeg->video_frame_count ++;
|
||||
// log::info("[VIDEO] frame:%p frame_size:%d pts:%ld(%f s)", frame, frame_size, pkt->pts, this->timebase_to_ms(this->get_video_timebase(), pkt->pts) / 1000);
|
||||
if (av_interleaved_write_frame(ffmpeg->format_context, pkt) < 0) {
|
||||
fprintf(stderr, "send frame failed!\r\n");
|
||||
av_packet_free(&pkt);
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
av_packet_unref(pkt);
|
||||
av_packet_free(&pkt);
|
||||
}
|
||||
} else {
|
||||
if (ffmpeg->has_audio) {
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
if (!pkt) {
|
||||
fprintf(stderr, "Can't malloc avpacket\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
if (frame && frame_size > 0) {
|
||||
auto pcm_list = ffmpeg->pcm_list;
|
||||
AVFrame *audio_frame = ffmpeg->audio_frame;
|
||||
AVStream *audio_stream = ffmpeg->audio_stream;
|
||||
AVCodecContext *audio_codec_ctx = ffmpeg->audio_codec_ctx;
|
||||
SwrContext *swr_ctx = ffmpeg->audio_swr_ctx;
|
||||
AVFormatContext *outputFormatContext = ffmpeg->format_context;
|
||||
AVPacket *audio_packet = pkt;
|
||||
size_t buffer_size = av_samples_get_buffer_size(NULL, ffmpeg->audio_channels, audio_frame->nb_samples, ffmpeg->audio_format, 1);
|
||||
size_t pcm_remain_len = frame_size;
|
||||
|
||||
// fill last pcm to buffer_size
|
||||
size_t next_pts = pts;
|
||||
if (!pcm_list->empty()) {
|
||||
auto last_item = pcm_list->back();
|
||||
Bytes *last_pcm = last_item.second;
|
||||
if (last_pcm && last_pcm->data_len < buffer_size) {
|
||||
int temp_size = pcm_remain_len + last_pcm->data_len >= buffer_size ? buffer_size : pcm_remain_len + last_pcm->data_len;
|
||||
uint8_t *temp = (uint8_t *)malloc(temp_size);
|
||||
err::check_null_raise(temp, "malloc failed!");
|
||||
memcpy(temp, last_pcm->data, last_pcm->data_len);
|
||||
if (pcm_remain_len + last_pcm->data_len < buffer_size) {
|
||||
memcpy(temp + last_pcm->data_len, frame, pcm_remain_len);
|
||||
pcm_remain_len = 0;
|
||||
} else {
|
||||
memcpy(temp + last_pcm->data_len, frame, buffer_size - last_pcm->data_len);
|
||||
pcm_remain_len -= (buffer_size - last_pcm->data_len);
|
||||
}
|
||||
|
||||
Bytes *new_pcm = new Bytes(temp, temp_size, true, false);
|
||||
pcm_list->pop_back();
|
||||
delete last_pcm;
|
||||
|
||||
size_t new_pts = last_item.first;
|
||||
next_pts = new_pts + get_audio_pts_from_pcm_size(new_pcm->data_len);
|
||||
pcm_list->push_back(std::make_pair(new_pts, new_pcm));
|
||||
}
|
||||
}
|
||||
|
||||
// fill other pcm
|
||||
while (pcm_remain_len > 0) {
|
||||
int temp_size = pcm_remain_len >= buffer_size ? buffer_size : pcm_remain_len;
|
||||
uint8_t *temp = (uint8_t *)malloc(temp_size);
|
||||
err::check_null_raise(temp, "malloc failed!");
|
||||
memcpy(temp, frame + frame_size - pcm_remain_len, temp_size);
|
||||
pcm_remain_len -= temp_size;
|
||||
|
||||
Bytes *new_pcm = new Bytes(temp, temp_size, true, false);
|
||||
pcm_list->push_back(std::make_pair(next_pts, new_pcm));
|
||||
next_pts += get_audio_pts_from_pcm_size(temp_size);
|
||||
}
|
||||
|
||||
// for (auto it = _ffmpeg.pcm_list->begin(); it != _ffmpeg.pcm_list->end(); ++it) {
|
||||
// auto &item = *it;
|
||||
// log::info("PTS:%d PCM:%p PCM_SIZE:%d", item.first, item.second->data, item.second->data_len);
|
||||
// }
|
||||
|
||||
// audio process
|
||||
while (pcm_list->size() > 0) {
|
||||
auto item = pcm_list->front();
|
||||
auto next_pts = item.first;
|
||||
Bytes *pcm = item.second;
|
||||
if (pcm) {
|
||||
if (pcm->data_len == buffer_size) {
|
||||
const uint8_t *in[] = {pcm->data};
|
||||
uint8_t *out[] = {audio_frame->data[0]};
|
||||
swr_convert(swr_ctx, out, audio_codec_ctx->frame_size, in, audio_codec_ctx->frame_size);
|
||||
audio_frame->pts = next_pts;
|
||||
if (avcodec_send_frame(audio_codec_ctx, audio_frame) < 0) {
|
||||
printf("Error sending audio_frame to encoder.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
while (avcodec_receive_packet(audio_codec_ctx, audio_packet) == 0) {
|
||||
audio_packet->stream_index = audio_stream->index;
|
||||
audio_packet->pts = audio_packet->dts = next_pts;
|
||||
audio_packet->duration = get_audio_pts_from_pcm_size(pcm->data_len);
|
||||
|
||||
av_interleaved_write_frame(outputFormatContext, audio_packet);
|
||||
av_packet_unref(audio_packet);
|
||||
}
|
||||
pcm_list->pop_front();
|
||||
delete pcm;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
err::check_raise(err::ERR_RUNTIME, "pcm data error");
|
||||
}
|
||||
}
|
||||
// log::info("pcm process time:%d", time::ticks_ms() - t);
|
||||
}
|
||||
|
||||
av_packet_unref(pkt);
|
||||
av_packet_free(&pkt);
|
||||
}
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
void reset_pts()
|
||||
{
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
ffmpeg->video_frame_count = 0;
|
||||
}
|
||||
|
||||
bool is_opened()
|
||||
{
|
||||
return _open;
|
||||
}
|
||||
|
||||
int get_frame_size_per_second() {
|
||||
if (!_open)
|
||||
return 0;
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
return ffmpeg->audio_frame->sample_rate * ffmpeg->audio_frame->channels * av_get_bytes_per_sample(ffmpeg->audio_format);
|
||||
}
|
||||
|
||||
std::vector<int> get_video_timebase() {
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
if (!_open || !ffmpeg->has_video)
|
||||
return std::vector<int>();
|
||||
return {ffmpeg->video_stream->time_base.num, ffmpeg->video_stream->time_base.den};
|
||||
}
|
||||
|
||||
std::vector<int> get_audio_timebase() {
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
if (!_open || !ffmpeg->has_audio)
|
||||
return std::vector<int>();
|
||||
return {ffmpeg->audio_stream->time_base.num, ffmpeg->audio_stream->time_base.den};
|
||||
}
|
||||
|
||||
double timebase_to_us(std::vector<int> timebase, uint64_t value) {
|
||||
if (timebase.size() < 2) return -1;
|
||||
return value * 1000000 / ((double)timebase[1] / timebase[0]);
|
||||
}
|
||||
|
||||
double timebase_to_ms(std::vector<int> timebase, uint64_t value) {
|
||||
if (timebase.size() < 2) return -1;
|
||||
return value * 1000 / ((double)timebase[1] / timebase[0]);
|
||||
}
|
||||
|
||||
uint64_t ms_to_pts(std::vector<int> timebase, uint64_t value) {
|
||||
return value * (timebase[1] / timebase[0]) / 1000;
|
||||
}
|
||||
|
||||
uint64_t get_audio_pts_from_pcm_size(size_t pcm_length) {
|
||||
ffmpeg_param_t *ffmpeg = (ffmpeg_param_t *)&_ffmpeg;
|
||||
if (!_open || !ffmpeg->has_audio)
|
||||
return 0;
|
||||
return pcm_length * (ffmpeg->audio_stream->time_base.den / ffmpeg->audio_stream->time_base.num) / get_frame_size_per_second();
|
||||
}
|
||||
};
|
||||
|
||||
namespace maix::rtmp {
|
||||
class PrivateParam {
|
||||
public:
|
||||
enum {
|
||||
RTMP_IDLE = 0,
|
||||
RTMP_STOP,
|
||||
RTMP_RUNNING,
|
||||
} status;
|
||||
RTMPClient *rtmp_client = nullptr;
|
||||
rtmp::Rtmp *rtmp;
|
||||
std::string url;
|
||||
int bitrate;
|
||||
camera::Camera **camera;
|
||||
audio::Recorder **recorder;
|
||||
video::Encoder **encoder;
|
||||
display::Display **display;
|
||||
};
|
||||
|
||||
Rtmp::Rtmp(std::string host, int port, std::string app, std::string stream, int bitrate) {
|
||||
_host = host;
|
||||
_port = port;
|
||||
_app = app;
|
||||
_stream = stream;
|
||||
_bitrate = bitrate;
|
||||
|
||||
RTMPClient *rtmp_client = new RTMPClient();
|
||||
PrivateParam *param = new PrivateParam();
|
||||
param->rtmp = this;
|
||||
param->rtmp_client = rtmp_client;
|
||||
param->status = PrivateParam::RTMP_IDLE;
|
||||
param->bitrate = bitrate;
|
||||
param->camera = &_camera;
|
||||
param->recorder = &_audio_recorder;
|
||||
param->encoder = &_video_encoder;
|
||||
param->display = &_display;
|
||||
/* rtmp://host:prot/app/stream */
|
||||
param->url = "rtmp://" + host + ":" + std::to_string(port) + "/" + app + "/" + stream;
|
||||
if (0 != pthread_mutex_init(&_lock, NULL)) {
|
||||
throw std::runtime_error("create lock failed!");
|
||||
}
|
||||
// log::info("url:%s", param->url.c_str());
|
||||
|
||||
_param = param;
|
||||
_thread = nullptr;
|
||||
_camera = nullptr;
|
||||
_start = false;
|
||||
_capture_image = nullptr;
|
||||
_need_capture = false;
|
||||
_path = std::string();
|
||||
}
|
||||
|
||||
Rtmp::~Rtmp() {
|
||||
if (_param) {
|
||||
PrivateParam *param = (PrivateParam *)_param;
|
||||
if (param->status != PrivateParam::RTMP_IDLE) {
|
||||
this->stop();
|
||||
}
|
||||
|
||||
if (param->rtmp_client) {
|
||||
delete param->rtmp_client;
|
||||
param->rtmp_client = nullptr;
|
||||
}
|
||||
delete param;
|
||||
_param = nullptr;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&_lock);
|
||||
}
|
||||
|
||||
// return 0 ok, other error
|
||||
int Rtmp::push_video(void *data, size_t data_size, uint32_t timestamp) {
|
||||
PrivateParam *param = (PrivateParam *)_param;
|
||||
if (param && param->rtmp_client) {
|
||||
auto pts = param->rtmp_client->ms_to_pts(param->rtmp_client->get_audio_timebase(), timestamp);
|
||||
param->rtmp_client->push((uint8_t *)data, data_size, pts);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// return 0 ok, other error
|
||||
int Rtmp::push_audio(void *data, size_t data_size, uint32_t timestamp) {
|
||||
PrivateParam *param = (PrivateParam *)_param;
|
||||
if (param && param->rtmp_client) {
|
||||
auto pts = param->rtmp_client->ms_to_pts(param->rtmp_client->get_audio_timebase(), timestamp);
|
||||
param->rtmp_client->push((uint8_t *)data, data_size, pts, true);
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// return 0 ok, other error
|
||||
int Rtmp::push_script(void *data, size_t data_size, uint32_t timestamp) {
|
||||
err::check_raise(err::ERR_NOT_IMPL, "This interface is not supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err::Err Rtmp::lock(uint32_t time) {
|
||||
uint32_t count = 0;
|
||||
while (0 != pthread_mutex_trylock(&_lock)) {
|
||||
count ++;
|
||||
if (count >= time) {
|
||||
break;
|
||||
}
|
||||
time::sleep_ms(1);
|
||||
}
|
||||
|
||||
if (count >= time)
|
||||
return err::ERR_BUSY;
|
||||
else
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err Rtmp::unlock() {
|
||||
if (0 == pthread_mutex_unlock(&_lock)) {
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
static void _push_camera_thread(void *priv)
|
||||
{
|
||||
PrivateParam *param = (PrivateParam *)priv;
|
||||
camera::Camera *camera = *param->camera;
|
||||
audio::Recorder *audio_recorder = *param->recorder;
|
||||
|
||||
param->status = PrivateParam::RTMP_IDLE;
|
||||
}
|
||||
|
||||
err::Err Rtmp::start(std::string path) {
|
||||
lock(-1);
|
||||
err::Err ret = err::ERR_NONE;
|
||||
PrivateParam *param = (PrivateParam *)_param;
|
||||
auto video_encoder_width = 0;
|
||||
auto video_encoder_height = 0;
|
||||
auto video_encoder_format = 0;
|
||||
auto fps = 30;
|
||||
bool has_audio = false;
|
||||
RTMPClient *rtmp_client = nullptr;
|
||||
|
||||
if (param == nullptr) {
|
||||
ret = err::ERR_RUNTIME;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
rtmp_client = (RTMPClient *)param->rtmp_client;
|
||||
if (param->status != PrivateParam::RTMP_IDLE) {
|
||||
ret = err::ERR_BUSY;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
if (_camera == nullptr) {
|
||||
log::info("You must use the bind_camera interface to bind a Camera object.");
|
||||
ret = err::ERR_NOT_READY;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
if (_audio_recorder == nullptr) {
|
||||
has_audio = false;
|
||||
} else {
|
||||
has_audio = true;
|
||||
_audio_recorder->reset(true);
|
||||
}
|
||||
|
||||
video_encoder_width = _camera->width();
|
||||
video_encoder_height = _camera->height();
|
||||
video_encoder_format = _camera->format();
|
||||
_camera->set_fps(fps);
|
||||
// log::info("Create encoder, width:%d height:%d format:%s fps:%d bitrate:%d", video_encoder_width, video_encoder_height, image::fmt_names[video_encoder_format].c_str(), fps, param->bitrate);
|
||||
_video_encoder = new video::Encoder("", video_encoder_width, video_encoder_height, (image::Format)video_encoder_format, video::VIDEO_H264, fps, 50, param->bitrate);
|
||||
if (!_video_encoder) {
|
||||
log::info("Create video encoder failed!");
|
||||
ret = err::ERR_RUNTIME;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
err::check_raise(rtmp_client->config("has_audio", has_audio), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config("video_codec_id", AV_CODEC_ID_H264), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config("video_width", video_encoder_width), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config("video_height", video_encoder_height), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config("video_bitrate", _bitrate), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config("video_fps", fps), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config("video_pixel_format", AV_PIX_FMT_NV21), "rtmp config failed!");
|
||||
err::check_raise(rtmp_client->config_url(param->url), "rtmp config failed!");
|
||||
|
||||
_thread = new thread::Thread(_push_camera_thread, param);
|
||||
if (this->_thread == NULL) {
|
||||
log::error("create camera thread failed!\r\n");
|
||||
ret = err::ERR_RUNTIME;
|
||||
goto _error;
|
||||
}
|
||||
|
||||
unlock();
|
||||
return err::ERR_NONE;
|
||||
_error:
|
||||
unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
err::Err Rtmp::stop() {
|
||||
PrivateParam *param = (PrivateParam *)_param;
|
||||
lock(-1);
|
||||
if (param->status != PrivateParam::RTMP_IDLE) {
|
||||
param->status = PrivateParam::RTMP_STOP;
|
||||
}
|
||||
|
||||
unlock();
|
||||
|
||||
if (_thread) {
|
||||
_thread->join();
|
||||
_thread = nullptr;
|
||||
}
|
||||
|
||||
while (param->status != PrivateParam::RTMP_IDLE) {
|
||||
time::sleep_ms(100);
|
||||
log::info("wait rtmp thread exit..");
|
||||
}
|
||||
|
||||
if (_video_encoder) {
|
||||
delete _video_encoder;
|
||||
_video_encoder = nullptr;
|
||||
}
|
||||
|
||||
if (_audio_recorder) {
|
||||
_audio_recorder->reset(false);
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
}
|
||||
642
components/vision/port/maixcam2/maix_rtsp_maixcam2.cpp
Normal file
642
components/vision/port/maixcam2/maix_rtsp_maixcam2.cpp
Normal file
@@ -0,0 +1,642 @@
|
||||
/**
|
||||
* @author lxowalle@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
#include "maix_rtsp.hpp"
|
||||
#include "maix_err.hpp"
|
||||
#include "maix_basic.hpp"
|
||||
#include "maix_audio.hpp"
|
||||
#include "maix_video.hpp"
|
||||
#include "maix_ffmpeg.hpp"
|
||||
#include <dirent.h>
|
||||
#include "maix_rtsp_server.hpp"
|
||||
#include <sys/types.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
namespace maix::rtsp
|
||||
{
|
||||
Region::Region(int x, int y, int width, int height, image::Format format, camera::Camera *camera)
|
||||
{
|
||||
if (format != image::Format::FMT_BGRA8888) {
|
||||
err::check_raise(err::ERR_RUNTIME, "region support FMT_BGRA8888 only!");
|
||||
}
|
||||
|
||||
if (camera == NULL) {
|
||||
err::check_raise(err::ERR_RUNTIME, "region bind a NULL camera!");
|
||||
}
|
||||
|
||||
this->_width = width;
|
||||
this->_height = height;
|
||||
this->_x = x;
|
||||
this->_y = y;
|
||||
this->_format = format;
|
||||
this->_camera = camera;
|
||||
}
|
||||
|
||||
Region::~Region() {
|
||||
|
||||
}
|
||||
|
||||
image::Image *Region::get_canvas() {
|
||||
void *data;
|
||||
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
err::Err Region::update_canvas() {
|
||||
image::Image *img = this->_image;
|
||||
if (img->format() == image::Format::FMT_BGRA8888) {
|
||||
uint32_t *data_u32 = (uint32_t *)img->data();
|
||||
int width = img->width();
|
||||
int height = img->height();
|
||||
|
||||
if (this->_flip) {
|
||||
for (int h = 0; h < height; h ++) {
|
||||
for (int w = 0; w < width / 2; w ++) {
|
||||
int left_idx = h * width + w;
|
||||
int right_idx = h * width + (width - 1 - w);
|
||||
uint32_t tmp = data_u32[left_idx];
|
||||
data_u32[left_idx] = data_u32[right_idx];
|
||||
data_u32[right_idx] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->_mirror) {
|
||||
for (int h = 0; h < height / 2; h ++) {
|
||||
for (int w = 0; w < width; w ++) {
|
||||
int left_idx = h * width + w;
|
||||
int right_idx = (height - 1 - h) * width + w;
|
||||
uint32_t tmp = data_u32[left_idx];
|
||||
data_u32[left_idx] = data_u32[right_idx];
|
||||
data_u32[right_idx] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::error("support FMT_BGRA888 only!\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
enum RtspStatus{
|
||||
RTSP_IDLE = 0,
|
||||
RTSP_RUNNING,
|
||||
RTSP_STOP,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
MaixRtspServer *rtsp_server;
|
||||
enum RtspStatus status;
|
||||
int *clients;
|
||||
camera::Camera *camera;
|
||||
video::Encoder *encoder;
|
||||
audio::Recorder *audio_recorder;
|
||||
ffmpeg::FFmpegPacker *ffmpeg_packer;
|
||||
bool bind_camera;
|
||||
bool bind_audio_recorder;
|
||||
int encoder_bitrate;
|
||||
int fps;
|
||||
int audio_bitrate;
|
||||
} rtsp_param_t;
|
||||
|
||||
Rtsp::Rtsp(std::string ip, int port, int fps, rtsp::RtspStreamType stream_type, int bitrate) {
|
||||
rtsp_param_t *param = (rtsp_param_t *)malloc(sizeof(rtsp_param_t));
|
||||
err::check_null_raise(param, "malloc failed!");
|
||||
memset(param, 0, sizeof(rtsp_param_t));
|
||||
|
||||
this->_ip = ip;
|
||||
this->_port = port;
|
||||
this->_fps = fps;
|
||||
this->_stream_type = stream_type;
|
||||
this->_is_start = false;
|
||||
this->_thread = NULL;
|
||||
this->_param = param;
|
||||
this->_region_max_number = 16;
|
||||
for (int i = 0; i < this->_region_max_number; i ++) {
|
||||
this->_region_list.push_back(NULL);
|
||||
this->_region_type_list.push_back(0);
|
||||
this->_region_used_list.push_back(false);
|
||||
}
|
||||
|
||||
if (_ip.size() == 0) {
|
||||
_ip = "0.0.0.0";
|
||||
}
|
||||
|
||||
this->_timestamp = 0;
|
||||
this->_last_ms = 0;
|
||||
|
||||
param->status = RTSP_IDLE;
|
||||
param->camera = nullptr;
|
||||
param->audio_recorder = nullptr;
|
||||
param->bind_camera = false;
|
||||
param->bind_audio_recorder = false;
|
||||
param->encoder_bitrate = bitrate;
|
||||
param->fps = fps;
|
||||
param->audio_bitrate = 128000;
|
||||
}
|
||||
|
||||
Rtsp::~Rtsp() {
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (param) {
|
||||
if (param->status != RTSP_IDLE) {
|
||||
this->stop();
|
||||
}
|
||||
|
||||
if (param->encoder) {
|
||||
delete param->encoder;
|
||||
param->encoder = nullptr;
|
||||
}
|
||||
|
||||
if (param->rtsp_server) {
|
||||
delete param->rtsp_server;
|
||||
param->rtsp_server = nullptr;
|
||||
}
|
||||
|
||||
free(_param);
|
||||
_param = nullptr;
|
||||
}
|
||||
|
||||
for (auto ®ion : this->_region_list) {
|
||||
delete region;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
static void dump_type(uint8_t *m_buf, int bytes_read)
|
||||
{
|
||||
int i = 0, start_code = 0;
|
||||
for (i=0; i<bytes_read-5; i++) {
|
||||
if(m_buf[i] == 0 && m_buf[i+1] == 0 && m_buf[i+2] == 1) {
|
||||
start_code = 3;
|
||||
|
||||
if (start_code > 0) {
|
||||
printf("[%d] nalu type:%d\r\n", i, m_buf[i+start_code]&0x1F);
|
||||
}
|
||||
|
||||
i += start_code;
|
||||
continue;
|
||||
}
|
||||
else if(m_buf[i] == 0 && m_buf[i+1] == 0 && m_buf[i+2] == 0 && m_buf[i+3] == 1) {
|
||||
start_code = 4;
|
||||
|
||||
if (start_code > 0) {
|
||||
printf("[%d] nalu type:%d\r\n", i, m_buf[i+start_code]&0x1F);
|
||||
}
|
||||
|
||||
i += start_code;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint64_t get_video_timestamp() {
|
||||
return time::ticks_ms() * 90;
|
||||
}
|
||||
|
||||
static uint64_t get_audio_timestamp() {
|
||||
return get_video_timestamp();
|
||||
}
|
||||
|
||||
static void _camera_push_thread(void *args) {
|
||||
rtsp_param_t *param = (rtsp_param_t *)args;
|
||||
MaixRtspServer *rtsp_server = param->rtsp_server;
|
||||
int vi_ch = param->camera->get_channel();
|
||||
int enc_ch = 1;
|
||||
size_t video_pts = 0, audio_pts = 0;
|
||||
uint64_t last_ms = time::ticks_ms();
|
||||
bool found_first_pps_sps = false;
|
||||
uint64_t last_read_pcm_ms = time::ticks_ms();
|
||||
|
||||
typedef struct {
|
||||
MaixRtspServer *rtsp_server;
|
||||
bool is_i_or_b;
|
||||
} temp_param_t;
|
||||
|
||||
while (param->status == RTSP_RUNNING && !app::need_exit()) {
|
||||
void *frame = NULL;
|
||||
bool found_camera_frame = false;
|
||||
bool found_venc_stream = false;
|
||||
bool found_audio_data = false;
|
||||
|
||||
// if (found_audio_data) {
|
||||
// delete pcm;
|
||||
// }
|
||||
}
|
||||
|
||||
param->status = RTSP_IDLE;
|
||||
}
|
||||
|
||||
err::Err Rtsp::start() {
|
||||
err::Err err = err::ERR_NONE;
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (!param) {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
if (param->status != RTSP_IDLE) {
|
||||
return err::ERR_BUSY;
|
||||
}
|
||||
|
||||
// check camera
|
||||
if (!param->bind_camera || !param->camera) {
|
||||
log::error("You need bind a camera!");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
if (param->camera->width() % 32 != 0) {
|
||||
log::error("camera width must be multiple of 32!\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
// create rtsp server
|
||||
MaixRtspServerBuilder rtsp_builder = MaixRtspServerBuilder()
|
||||
.set_ip(_ip)
|
||||
.set_port(this->_port)
|
||||
.set_session_name("live");
|
||||
if (param->bind_audio_recorder && param->audio_recorder) {
|
||||
rtsp_builder = rtsp_builder.set_audio(true)
|
||||
.set_audio_channels(param->audio_recorder->channel())
|
||||
.set_audio_sample_rate(param->audio_recorder->sample_rate());
|
||||
}
|
||||
param->rtsp_server = rtsp_builder.build();
|
||||
|
||||
// create encoder
|
||||
if (param->encoder) {
|
||||
delete param->encoder;
|
||||
param->encoder = nullptr;
|
||||
}
|
||||
param->encoder = new video::Encoder("", param->camera->width(), param->camera->height(), image::Format::FMT_YVU420SP, video::VIDEO_H264, param->fps, 50, param->encoder_bitrate);
|
||||
err::check_null_raise(param->encoder, "Create video encoder failed!");
|
||||
|
||||
// create frame package
|
||||
param->ffmpeg_packer = new ffmpeg::FFmpegPacker();
|
||||
err::check_null_raise(param->ffmpeg_packer, "ffmpeg packer init failed");
|
||||
err::check_bool_raise(!param->ffmpeg_packer->config2("context_format_name", "flv"), "rtmp config failed!");
|
||||
if (param->bind_audio_recorder && param->audio_recorder) {
|
||||
if (param->audio_recorder->format() != audio::FMT_S16_LE) {
|
||||
log::error("Only support audio::FMT_S16_LE format!");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
err::check_bool_raise(!param->ffmpeg_packer->config("has_audio", true), "rtmp config failed!");
|
||||
err::check_bool_raise(!param->ffmpeg_packer->config("audio_sample_rate", param->audio_recorder->sample_rate()), "rtmp config failed!");
|
||||
err::check_bool_raise(!param->ffmpeg_packer->config("audio_channels", param->audio_recorder->channel()), "rtmp config failed!");
|
||||
err::check_bool_raise(!param->ffmpeg_packer->config("audio_bitrate", param->audio_bitrate), "rtmp config failed!");
|
||||
err::check_bool_raise(!param->ffmpeg_packer->config("audio_format", AV_SAMPLE_FMT_S16), "rtmp config failed!");
|
||||
}
|
||||
param->ffmpeg_packer->open();
|
||||
|
||||
// start audio
|
||||
if (param->bind_audio_recorder) {
|
||||
param->audio_recorder->reset(true);
|
||||
}
|
||||
|
||||
// create runtime thread
|
||||
param->status = RTSP_RUNNING;
|
||||
_thread = new thread::Thread(_camera_push_thread, param);
|
||||
if (_thread == NULL) {
|
||||
log::error("create camera thread failed!\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
err::Err Rtsp::stop() {
|
||||
err::Err err = err::ERR_NONE;
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (param->status != RTSP_RUNNING) {
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
param->status = RTSP_STOP;
|
||||
if (_thread) {
|
||||
_thread->join();
|
||||
_thread = nullptr;
|
||||
}
|
||||
|
||||
while (param->status != RTSP_IDLE) {
|
||||
time::sleep_ms(100);
|
||||
log::info("wait rtsp thread exit..");
|
||||
}
|
||||
|
||||
// stop audio
|
||||
if (param->bind_audio_recorder) {
|
||||
param->audio_recorder->reset(false);
|
||||
}
|
||||
|
||||
if (param->ffmpeg_packer) {
|
||||
delete param->ffmpeg_packer;
|
||||
param->ffmpeg_packer = nullptr;
|
||||
}
|
||||
|
||||
if (param->encoder) {
|
||||
delete param->encoder;
|
||||
param->encoder = nullptr;
|
||||
}
|
||||
|
||||
if (param->rtsp_server) {
|
||||
delete param->rtsp_server;
|
||||
param->rtsp_server = nullptr;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
err::Err Rtsp::bind_camera(camera::Camera *camera) {
|
||||
err::Err err = err::ERR_NONE;
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (!param) {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
if (camera->format() != image::Format::FMT_YVU420SP) {
|
||||
err::check_raise(err::ERR_RUNTIME, "bind camera failed! support FMT_YVU420SP only!\r\n");
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
param->camera = camera;
|
||||
param->bind_camera = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
err::Err Rtsp::bind_audio_recorder(audio::Recorder *recorder) {
|
||||
err::Err err = err::ERR_NONE;
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (!param) {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
param->audio_recorder = recorder;
|
||||
param->bind_audio_recorder = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
err::Err Rtsp::write(video::Frame &frame) {
|
||||
err::Err err = err::ERR_NONE;
|
||||
err::check_raise(err::ERR_NOT_IMPL, "write frame not impl!");
|
||||
return err;
|
||||
}
|
||||
|
||||
camera::Camera *Rtsp::to_camera() {
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
err::check_null_raise(param->camera, "camera is null!");
|
||||
return param->camera;
|
||||
}
|
||||
|
||||
std::string Rtsp::get_url() {
|
||||
return "rtsp://" + _ip + ":" + std::to_string(_port) + "/live";
|
||||
}
|
||||
|
||||
|
||||
static int get_ip(char *hw, char ip[16])
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
int family, s;
|
||||
char host[NI_MAXHOST];
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1) {
|
||||
perror("getifaddrs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
family = ifa->ifa_addr->sa_family;
|
||||
|
||||
if (family == AF_INET) {
|
||||
s = getnameinfo(ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) :
|
||||
sizeof(struct sockaddr_in6), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
||||
if (s != 0) {
|
||||
printf("getnameinfo() failed: %s\n", gai_strerror(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strcmp(ifa->ifa_name, hw)) {
|
||||
strncpy(ip, host, 16);
|
||||
freeifaddrs(ifaddr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static std::vector<std::string> rtsp_get_server_urls(std::string ip, int port)
|
||||
{
|
||||
char new_ip[16] = {0};
|
||||
|
||||
std::vector<std::string> ip_list;
|
||||
|
||||
if (!strcmp("0.0.0.0", ip.c_str())) {
|
||||
if (!get_ip((char *)"eth0", new_ip)) {
|
||||
ip_list.push_back("rtsp://" + std::string(new_ip) + ":" + std::to_string(port) + "/live");
|
||||
}
|
||||
if (!get_ip((char *)"usb0", new_ip)) {
|
||||
ip_list.push_back("rtsp://" + std::string(new_ip) + ":" + std::to_string(port) + "/live");
|
||||
}
|
||||
if (!get_ip((char *)"wlan0", new_ip)) {
|
||||
ip_list.push_back("rtsp://" + std::string(new_ip) + ":" + std::to_string(port) + "/live");
|
||||
}
|
||||
} else {
|
||||
ip_list.push_back("rtsp://" + ip + ":" + std::to_string(port) + "/live");
|
||||
}
|
||||
|
||||
return ip_list;
|
||||
}
|
||||
|
||||
std::vector<std::string> Rtsp::get_urls()
|
||||
{
|
||||
return rtsp_get_server_urls(_ip, _port);
|
||||
}
|
||||
|
||||
rtsp::Region *Rtsp::add_region(int x, int y, int width, int height, image::Format format) {
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (!param) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (format != image::Format::FMT_BGRA8888) {
|
||||
log::error("region support FMT_BGRA8888 only!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!param->bind_camera) {
|
||||
log::error("You must use bind camera firstly!\r\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find unused idx
|
||||
int unused_idx = -1;
|
||||
for (int i = 0; i < this->_region_max_number; i ++) {
|
||||
if (this->_region_used_list[i] == false) {
|
||||
unused_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
err::check_bool_raise(unused_idx != -1, "Unused region not found");
|
||||
|
||||
// Create region
|
||||
rtsp::Region *region = new rtsp::Region(x, y, width, height, format, param->camera);
|
||||
err::check_null_raise(region, "Create region failed!");
|
||||
this->_region_list[unused_idx] = region;
|
||||
this->_region_used_list[unused_idx] = true;
|
||||
this->_region_type_list[unused_idx] = 0;
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
err::Err Rtsp::update_region(rtsp::Region ®ion) {
|
||||
return region.update_canvas();
|
||||
}
|
||||
|
||||
err::Err Rtsp::del_region(rtsp::Region *region) {
|
||||
err::check_null_raise(region, "The region object is NULL");
|
||||
|
||||
for (int i = 0; i < this->_region_max_number; i ++) {
|
||||
if (this->_region_list[i] == region) {
|
||||
this->_region_list[i] = NULL;
|
||||
this->_region_used_list[i] = false;
|
||||
this->_region_type_list[i] = 0;
|
||||
delete region;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err Rtsp::draw_rect(int id, int x, int y, int width, int height, image::Color color, int thickness)
|
||||
{
|
||||
rtsp_param_t *param = (rtsp_param_t *)_param;
|
||||
if (!param) {
|
||||
return err::ERR_RUNTIME;
|
||||
}
|
||||
|
||||
// Check id
|
||||
if (id < 0 || id > 3) {
|
||||
log::error("region id is invalid! range is [0, 3");
|
||||
err::check_raise(err::ERR_RUNTIME, "invalid parameter");
|
||||
}
|
||||
|
||||
if (x < 0) {
|
||||
width = width + x < 0 ? 0 : width + x;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
height = height + y < 0 ? 0 : height + y;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
if (x > param->camera->width()) {
|
||||
x = 0;
|
||||
width = 0;
|
||||
}
|
||||
|
||||
if (y > param->camera->height()) {
|
||||
y = 0;
|
||||
height = 0;
|
||||
}
|
||||
|
||||
if (x + width > param->camera->width()) {
|
||||
width = param->camera->width() - x;
|
||||
}
|
||||
|
||||
if (y + height > param->camera->height()) {
|
||||
height = param->camera->height() - y;
|
||||
}
|
||||
|
||||
// Check if the region [id, id + 4) is used for other functions
|
||||
for (size_t i = id; i < this->_region_used_list.size() && (int)i < id + 4; i ++) {
|
||||
if (_region_used_list[i] == true && _region_type_list[i] != 2) {
|
||||
log::error("In areas %d - %d, %d is used for other functions(%d)", id, id + 4, i, _region_type_list[i]);
|
||||
err::check_raise(err::ERR_RUNTIME, "invalid parameter");
|
||||
}
|
||||
}
|
||||
|
||||
// Delete last region
|
||||
for (size_t i = id; i < this->_region_used_list.size() && (int)i < id + 4; i ++) {
|
||||
if (_region_used_list[i] == true && _region_type_list[i] == 2) {
|
||||
delete _region_list[i];
|
||||
_region_list[i] = NULL;
|
||||
_region_used_list[i] = false;
|
||||
_region_type_list[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create upper region
|
||||
int upper_lower_height = 0;
|
||||
upper_lower_height = thickness > 0 ? thickness : height;
|
||||
upper_lower_height = upper_lower_height > height ? height : upper_lower_height;
|
||||
rtsp::Region *region_upper = add_region(x, y, width, upper_lower_height);
|
||||
err::check_null_raise(region_upper);
|
||||
|
||||
// Create lower region
|
||||
rtsp::Region *region_lower = add_region(x, y + height - upper_lower_height, width, upper_lower_height);
|
||||
err::check_null_raise(region_upper);
|
||||
|
||||
// Create left region
|
||||
int left_right_width = 0;
|
||||
left_right_width = thickness > 0 ? thickness : width;
|
||||
left_right_width = left_right_width > width ? width : left_right_width;
|
||||
rtsp::Region *region_left = add_region(x, y + upper_lower_height, left_right_width, height - 2 * upper_lower_height);
|
||||
err::check_null_raise(region_left);
|
||||
|
||||
// Create right region
|
||||
rtsp::Region *region_right = add_region(x + width - left_right_width, y + upper_lower_height, left_right_width, height - 2 * upper_lower_height);
|
||||
err::check_null_raise(region_right);
|
||||
|
||||
// Config region list
|
||||
_region_list[id] = region_upper;
|
||||
_region_list[id + 1] = region_lower;
|
||||
_region_list[id + 2] = region_left;
|
||||
_region_list[id + 3] = region_right;
|
||||
_region_used_list[id] = true;
|
||||
_region_used_list[id + 1] = true;
|
||||
_region_used_list[id + 2] = true;
|
||||
_region_used_list[id + 3] = true;
|
||||
_region_type_list[id] = 2;
|
||||
_region_type_list[id + 1] = 2;
|
||||
_region_type_list[id + 2] = 2;
|
||||
_region_type_list[id + 3] = 2;
|
||||
|
||||
// Draw all of region
|
||||
uint32_t color_hex = color.hex();
|
||||
for (int i = id; i < id + 4; i ++) {
|
||||
rtsp::Region *region = _region_list[i];
|
||||
image::Image *img = region->get_canvas();
|
||||
err::check_null_raise(img, "Get canvas image failed!");
|
||||
uint32_t *data = (uint32_t *)img->data();
|
||||
int width = img->width();
|
||||
int height = img->height();
|
||||
for (int i = 0; i < height; i ++) {
|
||||
for (int j = 0; j < width; j ++) {
|
||||
data[i * width + j] = color_hex;
|
||||
}
|
||||
}
|
||||
update_region(*region);
|
||||
}
|
||||
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err Rtsp::draw_string(int id, int x, int y, const char *str, image::Color color, int size, int thickness)
|
||||
{
|
||||
return err::ERR_NOT_IMPL;
|
||||
}
|
||||
}
|
||||
236
components/vision/port/maixcam2/maix_touchscreen_maixcam2.hpp
Normal file
236
components/vision/port/maixcam2/maix_touchscreen_maixcam2.hpp
Normal file
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
* @author neucrack@sipeed
|
||||
* @copyright Sipeed Ltd 2023-
|
||||
* @license Apache 2.0
|
||||
* @update 2023.9.8: Add framework, create this file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "maix_basic.hpp"
|
||||
#include "maix_touchscreen_base.hpp"
|
||||
#include <linux/input.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
namespace maix::touchscreen
|
||||
{
|
||||
|
||||
class TouchScreen_MaixCam final : public TouchScreen_Base
|
||||
{
|
||||
public:
|
||||
TouchScreen_MaixCam(const std::string &device = "/dev/input/event1")
|
||||
{
|
||||
_opened = false;
|
||||
_fd = -1;
|
||||
_device = device;
|
||||
}
|
||||
|
||||
err::Err open()
|
||||
{
|
||||
if(_fd > 0)
|
||||
{
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
_opened = false;
|
||||
_fd = ::open(_device.c_str(), O_RDONLY);
|
||||
if(_fd < 0)
|
||||
{
|
||||
log::error("open touch screen failed: %s", _device.c_str());
|
||||
return err::ERR_IO;
|
||||
}
|
||||
|
||||
_x = 0;
|
||||
_y = 0;
|
||||
_pressed = false;
|
||||
|
||||
// set unblock read
|
||||
int non_blocking = 1;
|
||||
ioctl(_fd, FIONBIO, &non_blocking);
|
||||
|
||||
// char * absval[6] = { "Value", "Min", "Max", "Fuzz", "Flat", "Resolution" };
|
||||
int absX[6] = {};
|
||||
int absY[6] = {};
|
||||
|
||||
ioctl(_fd, EVIOCGABS(ABS_MT_POSITION_X), absX);
|
||||
ioctl(_fd, EVIOCGABS(ABS_MT_POSITION_Y), absY);
|
||||
|
||||
_x_max = absX[2];
|
||||
_y_max = absY[2];
|
||||
if(_x_max <= 0 || _y_max <= 0)
|
||||
{
|
||||
log::error("get touchscreen resolution failed");
|
||||
_x_max = 368;
|
||||
_y_max = 552;
|
||||
}
|
||||
|
||||
_init_epoll(_fd);
|
||||
|
||||
_opened = true;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
err::Err close()
|
||||
{
|
||||
if(_fd > 0)
|
||||
{
|
||||
::close(_fd);
|
||||
_fd = -1;
|
||||
}
|
||||
_opened = false;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
bool is_opened() {
|
||||
return _opened;
|
||||
}
|
||||
|
||||
err::Err read(int &x, int &y, bool &pressed)
|
||||
{
|
||||
err::Err e = _read(true);
|
||||
if(e != err::ERR_NONE)
|
||||
return e;
|
||||
x = _x;
|
||||
y = _y;
|
||||
pressed = _pressed;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
std::vector<int> read()
|
||||
{
|
||||
_read(true);
|
||||
return {_x, _y, _pressed ? 1 : 0};
|
||||
}
|
||||
|
||||
err::Err read0(int &x, int &y, bool &pressed)
|
||||
{
|
||||
err::Err e = _read(false);
|
||||
if(e != err::ERR_NONE)
|
||||
return e;
|
||||
x = _x;
|
||||
y = _y;
|
||||
pressed = _pressed;
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
|
||||
std::vector<int> read0()
|
||||
{
|
||||
_read(false);
|
||||
return {_x, _y, _pressed ? 1 : 0};
|
||||
}
|
||||
|
||||
bool available(int timeout)
|
||||
{
|
||||
return _available(timeout);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
int _x;
|
||||
int _y;
|
||||
bool _pressed;
|
||||
int _width;
|
||||
int _height;
|
||||
bool _opened;
|
||||
int _fd;
|
||||
std::string _device;
|
||||
int _x_max;
|
||||
int _y_max;
|
||||
int _epoll_fd;
|
||||
|
||||
void _init_epoll(int fd)
|
||||
{
|
||||
_epoll_fd = epoll_create(1);
|
||||
if(_epoll_fd < 0)
|
||||
{
|
||||
log::error("create epoll failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
struct epoll_event ev;
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = fd;
|
||||
if(epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
|
||||
{
|
||||
log::error("epoll_ctl add failed: %s", strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool _available(int timeout)
|
||||
{
|
||||
struct epoll_event events[1];
|
||||
int nfds = epoll_wait(_epoll_fd, events, 1, timeout);
|
||||
return nfds > 0;
|
||||
}
|
||||
|
||||
err::Err _read(bool empty_buffer)
|
||||
{
|
||||
struct input_event event;
|
||||
// struct epoll_event events[1];
|
||||
bool event_press = false;
|
||||
bool event_move = false;
|
||||
while(1)
|
||||
{
|
||||
// int nfds = epoll_wait(_epoll_fd, events, 1, 0);
|
||||
// if(nfds <= 0)
|
||||
// {
|
||||
// return err::ERR_NOT_READY;
|
||||
// }
|
||||
int ret = ::read(_fd, &event, sizeof(event));
|
||||
if(ret != sizeof(event))
|
||||
{
|
||||
if(event_move)
|
||||
return err::ERR_NONE;
|
||||
if(errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
return err::ERR_NOT_READY;
|
||||
log::error("read touch screen failed: %s, %d", strerror(errno), ret);
|
||||
return err::ERR_IO;
|
||||
}
|
||||
// printf("event %d %d\n", event.type, event.code);
|
||||
if(event.type == EV_ABS)
|
||||
{
|
||||
if(event.code == ABS_MT_POSITION_X || event.code == ABS_X)
|
||||
{
|
||||
// _x = event.value;
|
||||
// anti-clockwise 90 degree
|
||||
_y = event.value;
|
||||
event_move = true;
|
||||
}
|
||||
else if(event.code == ABS_MT_POSITION_Y || event.code == ABS_Y)
|
||||
{
|
||||
// _y = event.value;
|
||||
// anti-clockwise 90 degree
|
||||
_x = _y_max - event.value - 1;
|
||||
event_move = true;
|
||||
}
|
||||
}
|
||||
else if(event.type == EV_KEY)
|
||||
{
|
||||
if(event.code == BTN_TOUCH)
|
||||
{
|
||||
_pressed = event.value == 1;
|
||||
event_press = true;
|
||||
}
|
||||
}
|
||||
else if(event.type == EV_SYN)
|
||||
{
|
||||
if(event_press)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(event_move && !empty_buffer)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return err::ERR_NONE;
|
||||
}
|
||||
};
|
||||
} // namespace maix::touchscreen
|
||||
|
||||
|
||||
|
||||
1431
components/vision/port/maixcam2/maix_video_maixcam2.cpp
Normal file
1431
components/vision/port/maixcam2/maix_video_maixcam2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,10 @@
|
||||
#include "maix_display_mmf.hpp"
|
||||
#include "maix_display_fb.hpp"
|
||||
#endif
|
||||
#ifdef PLATFORM_MAIXCAM2
|
||||
#include "maix_display_maixcam2.hpp"
|
||||
#include "maix_display_fb.hpp"
|
||||
#endif
|
||||
|
||||
namespace maix::display
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ list(APPEND ADD_INCLUDE "include"
|
||||
)
|
||||
if(PLATFORM_LINUX)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/linux")
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
list(APPEND ADD_PRIVATE_INCLUDE "port/maixcam")
|
||||
endif()
|
||||
###############################################
|
||||
@@ -20,7 +20,7 @@ endif()
|
||||
append_srcs_dir(ADD_SRCS "src") # append source file in src dir to var ADD_SRCS
|
||||
if(PLATFORM_LINUX)
|
||||
append_srcs_dir(ADD_SRCS "port/linux")
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
append_srcs_dir(ADD_SRCS "port/maixcam")
|
||||
endif()
|
||||
# list(REMOVE_ITEM COMPONENT_SRCS "src/test.c")
|
||||
@@ -35,7 +35,7 @@ endif()
|
||||
list(APPEND ADD_REQUIREMENTS basic)
|
||||
if(PLATFORM_LINUX)
|
||||
list(APPEND ADD_REQUIREMENTS )
|
||||
elseif(PLATFORM_MAIXCAM)
|
||||
elseif(PLATFORM_MAIXCAM OR PLATFORM_MAIXCAM2)
|
||||
list(APPEND ADD_REQUIREMENTS tinyalsa)
|
||||
endif()
|
||||
###############################################
|
||||
|
||||
@@ -10,5 +10,5 @@ toolchain:
|
||||
path: toolchains/maixcam2
|
||||
bin_path: toolchains/maixcam2/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin
|
||||
prefix: aarch64-none-linux-gnu-
|
||||
c_flags: ''
|
||||
cxx_flags: ''
|
||||
c_flags: '-ldl'
|
||||
cxx_flags: '-ldl'
|
||||
Reference in New Issue
Block a user