diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a0fddc0..a69b19d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -98,3 +98,33 @@ jobs: - name: Test run: ctest -C ${{ env.BUILD_TYPE }} + + fullhan: + environment: ECR + needs: ecr-prepare + runs-on: ubuntu-latest + strategy: + matrix: + container: [ "482862934379.dkr.ecr.us-east-1.amazonaws.com/fullhan:6.5.0" ] + container: + image: ${{ matrix.container }} + credentials: + username: ${{ secrets.ECR_USERNAME }} + password: ${{ secrets.ECR_PASSWORD }} + + steps: + - name: Git checkout + uses: actions/checkout@v2 + + - name: Prepare board SDK + run: | + cp -r /FH8626V100/* ${GITHUB_WORKSPACE}/3rdparty/FH8626V100/ + + - name: Configure CMake + run: cmake -B ${{ github.workspace }}/build -DBOARD=FH8626V100 -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DBUILD_WEBRTC_SAMPLES=ON -DBUILD_KVS_SAMPLES=ON -DBUILD_SAVE_FRAME_SAMPLES=ON + + - name: Build + run: cmake --build ${{ github.workspace }}/build --config ${{ env.BUILD_TYPE }} --parallel 4 + + - name: Test + run: ctest -C ${{ env.BUILD_TYPE }} diff --git a/.gitignore b/.gitignore index 7b3f150..347f4bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ 3rdparty/T31/ 3rdparty/V4L2/ +3rdparty/FH8626V100/ build/ diff --git a/3rdparty/FH8626V100/.gitkeep b/3rdparty/FH8626V100/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/3rdparty/README.md b/3rdparty/README.md index 0f3e0c0..6e1840b 100644 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -55,3 +55,14 @@ T31/ |-- libimp-samples `-- libsysutils-samples ``` + +# FH8626V100 + +User need to put FH8626V100 board SDK under [3rdparty/FH8626V100/](3rdparty/FH8626V100/): + +``` +FH8626V100/ +├── common +├── include +└── lib +``` diff --git a/CMake/FH8626V100.cmake b/CMake/FH8626V100.cmake new file mode 100644 index 0000000..18a5fc1 --- /dev/null +++ b/CMake/FH8626V100.cmake @@ -0,0 +1,57 @@ +if(BOARD STREQUAL "FH8626V100") + add_compile_definitions(__LINUX_OS__) + add_compile_definitions(CONFIG_ARCH_FH8626V100) + + if(NOT SENSOR OR SENSOR STREQUAL "") + message(STATUS "No SENSOR was selected. Use jxf37_mipi by default.") + set(SENSOR "jxf37_mipi") + endif() + + set(BOARD_SDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/${BOARD}) + + set(BOARD_SRCS + ${BOARD_SDK_DIR}/common/components/libpes/src/libpes.c + ${BOARD_SDK_DIR}/common/sample_common/isp/src/start_sensor/start_sensor_v1.c + ${BOARD_SDK_DIR}/common/sample_common/isp/src/start_sensor/start_sensor_v2.c + ) + + aux_source_directory(${BOARD_SDK_DIR}/common/sample_common/isp/src BOARD_SRCS) + aux_source_directory(${BOARD_SDK_DIR}/common/sample_common/dsp/src BOARD_SRCS) + aux_source_directory(${BOARD_SDK_DIR}/common/sample_common/bgm/src BOARD_SRCS) + aux_source_directory(${BOARD_SDK_DIR}/common/components/libdbi_over_tcp/src BOARD_SRCS) + aux_source_directory(${BOARD_SDK_DIR}/common/components/libmisc/src BOARD_SRCS) + aux_source_directory(${BOARD_SDK_DIR}/common/vlcview BOARD_SRCS) + + set(BOARD_INCS_DIR + ${BOARD_SDK_DIR}/include/ + ${BOARD_SDK_DIR}/include/dsp/ + ${BOARD_SDK_DIR}/include/dsp_ext/ + ${BOARD_SDK_DIR}/include/isp/ + ${BOARD_SDK_DIR}/include/isp_ext/ + ${BOARD_SDK_DIR}/include/linux/ + ${BOARD_SDK_DIR}/include/types/ + ${BOARD_SDK_DIR}/common/ + ${BOARD_SDK_DIR}/common/components/ + ${BOARD_SDK_DIR}/common/components/libdbi_over_tcp/include/ + ${BOARD_SDK_DIR}/common/sample_common/ + ${BOARD_SDK_DIR}/common/vlcview/ + ) + + set(USE_MUCLIBC ON) + set(BOARD_DESTINATION_PLATFORM arm-unknown-linux-uclibc) + + link_directories(${BOARD_SDK_DIR}/lib/static ${BOARD_SDK_DIR}/lib/dynamic) + + set(BOARD_LIBS_SHARED + pthread rt m + advapi dsp isp ispcore dbi dci ${SENSOR} + vmm vlcview mipi + acw_mpi audio_codec + ) + set(BOARD_LIBS_STATIC + pthread rt m + libadvapi.a libdsp.a libisp.a libispcore.a libdbi.a libdci.a lib${SENSOR}.a + libvmm.a libvlcview.a libmipi.a + libacw_mpi.a libaudio_codec.a + ) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f2e822..c134d58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ elseif(BOARD STREQUAL "V4L2") message(STATUS "Selected board V4L2") elseif(BOARD STREQUAL "T31") message(STATUS "Selected board T31") +elseif(BOARD STREQUAL "FH8626V100") + message(STATUS "Selected board FH8626V100") else() message(FATAL "${BOARD} is not implemented yet.") endif() diff --git a/README.md b/README.md index 83fe133..bb140e6 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - x86/x64(By selecting board `V4L2` or `FILE`) - RPi(By selecting board `V4L2` or `FILE`) - T31(By selecting board `T31` or `FILE`) +- FH8626V100(By selecting board `FH8626V100` or `FILE`) ## Getting started with out-of-box KVS WebRTC sample diff --git a/samples/kvs/CMakeLists.txt b/samples/kvs/CMakeLists.txt index 4eecd86..221507f 100644 --- a/samples/kvs/CMakeLists.txt +++ b/samples/kvs/CMakeLists.txt @@ -21,6 +21,9 @@ ExternalProject_Add(kvs-producer TEST_COMMAND "" ) +include(CheckIncludeFiles) +check_include_files(signal.h HAVE_SIGNAL_H) + set(KVS_SAMPLE_SRCS ${CMAKE_CURRENT_LIST_DIR}/source/kvsappcli.c ${CMAKE_CURRENT_LIST_DIR}/source/option_configuration.c) @@ -60,3 +63,8 @@ add_dependencies(kvsproducer-static kvs-producer embedded-media-static) target_include_directories(kvsproducer-static PRIVATE ${AWS_DEPENDENCIES_DIR}/kvs/include/ ${EMBEDDED_MEDIA_INCLUDES_DIR}) target_link_directories(kvsproducer-static PRIVATE ${AWS_DEPENDENCIES_DIR}/kvs/lib/ ${EMBEDDED_MEDIA_LINK_DIR}) target_link_libraries(kvsproducer-static embedded-media-static ${KVS_SDK_LIBS_STATIC} ${BOARD_LIBS_STATIC}) + +if(HAVE_SIGNAL_H) + target_compile_definitions(kvsproducer-shared PRIVATE HAVE_SIGNAL_H) + target_compile_definitions(kvsproducer-static PRIVATE HAVE_SIGNAL_H) +endif() diff --git a/samples/kvs/source/kvsappcli.c b/samples/kvs/source/kvsappcli.c index f5772ab..32b2f62 100644 --- a/samples/kvs/source/kvsappcli.c +++ b/samples/kvs/source/kvsappcli.c @@ -36,7 +36,7 @@ #define ERRNO_NONE 0 #define ERRNO_FAIL __LINE__ -#define VIDEO_FRAME_BUFFER_SIZE_BYTES (128 * 1024UL) +#define VIDEO_FRAME_BUFFER_SIZE_BYTES (160 * 1024UL) #if ENABLE_AUDIO_TRACK #if USE_AUDIO_G711 #define AUDIO_FRAME_BUFFER_SIZE_BYTES (320UL) diff --git a/source/FH8626V100/FH8626V100AudioCapturer.c b/source/FH8626V100/FH8626V100AudioCapturer.c new file mode 100644 index 0000000..b0a37a6 --- /dev/null +++ b/source/FH8626V100/FH8626V100AudioCapturer.c @@ -0,0 +1,350 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include + +#include "com/amazonaws/kinesis/video/capturer/AudioCapturer.h" + +#include "FH8626V100Common.h" +#include "sample_common.h" + +#define USING_HARD_STREAM_AUDIO + +#define HANDLE_GET(x) FH8626V100AudioCapturer* audioHandle = (FH8626V100AudioCapturer*) ((x)) + +#define DEFAULT_PERIOD_SIZE 1024 +#define DEFAULT_VOLUME 28 +#define DEFAULT_MICIN_VOL 2 +#define DEFAULT_AIN_VOL 28 + +typedef struct { + AudioCapturerStatus status; + AudioCapability capability; + AudioFormat format; + AudioChannel channel; + AudioBitDepth bitDepth; + AudioSampleRate sampleRate; + AAC_ENC_HANDLE aacHandle; +} FH8626V100AudioCapturer; + +static int setStatus(AudioCapturerHandle handle, const AudioCapturerStatus newStatus) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + if (newStatus != audioHandle->status) { + audioHandle->status = newStatus; + KVS_LOG("AudioCapturer set new status[%d]\n", newStatus); + } + + return 0; +} + +AudioCapturerHandle audioCapturerCreate(void) +{ + FH8626V100AudioCapturer* audioHandle = NULL; + + if (!(audioHandle = (FH8626V100AudioCapturer*) malloc(sizeof(FH8626V100AudioCapturer)))) { + KVS_LOG("OOM"); + return NULL; + } + + memset(audioHandle, 0, sizeof(FH8626V100AudioCapturer)); + + audioHandle->capability.formats = (1 << (AUD_FMT_G711A - 1)) | (1 << (AUD_FMT_PCM - 1)) | (1 << (AUD_FMT_AAC - 1)); + audioHandle->capability.channels = (1 << (AUD_CHN_MONO - 1)); + audioHandle->capability.sampleRates = (1 << (AUD_SAM_8K - 1)) | (1 << (AUD_SAM_16K - 1)) | (1 << (AUD_SAM_32K - 1)); + audioHandle->capability.bitDepths = (1 << (AUD_BIT_16 - 1)); + + setStatus((AudioCapturerHandle) audioHandle, AUD_CAP_STATUS_STREAM_OFF); + +#ifdef USING_HARD_STREAM_AUDIO + if (FH_AC_Init()) { + KVS_LOG("FH_AC_Init err\n"); + return NULL; + } +#endif + + return (AudioCapturerHandle) audioHandle; +} + +AudioCapturerStatus audioCapturerGetStatus(const AudioCapturerHandle const handle) +{ + if (!handle) { + return AUD_CAP_STATUS_NOT_READY; + } + + HANDLE_GET(handle); + return audioHandle->status; +} + +int audioCapturerGetCapability(const AudioCapturerHandle const handle, AudioCapability* pCapability) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + if (!pCapability) { + return -EAGAIN; + } + + *pCapability = audioHandle->capability; + + return 0; +} + +int audioCapturerSetFormat(AudioCapturerHandle handle, const AudioFormat format, const AudioChannel channel, const AudioSampleRate sampleRate, + const AudioBitDepth bitDepth) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + HANDLE_STATUS_CHECK(audioHandle, AUD_CAP_STATUS_STREAM_OFF); + + FH_AC_CONFIG ac_config; + + switch (format) { + case AUD_FMT_PCM: + case AUD_FMT_G711A: + case AUD_FMT_AAC: + ac_config.enc_type = FH_PT_LPCM; // only support raw PCM format + break; + + default: + KVS_LOG("Unsupported format %d", format); + return -EINVAL; + } + + switch (channel) { + case AUD_CHN_MONO: + ac_config.channels = 1; // //only support one channel + break; + + default: + KVS_LOG("Unsupported channel num %d", channel); + return -EINVAL; + } + + switch (sampleRate) { + case AUD_SAM_8K: + ac_config.sample_rate = AC_SR_8K; + break; + + case AUD_SAM_16K: + ac_config.sample_rate = AC_SR_16K; + break; + + case AC_SR_32K: + ac_config.sample_rate = AC_SR_32K; + break; + + default: + KVS_LOG("Unsupported sample rate %d", sampleRate); + return -EINVAL; + } + + switch (bitDepth) { + case AUD_BIT_16: + ac_config.bit_width = AC_BW_16; + break; + + default: + KVS_LOG("Unsupported bit depth %d", bitDepth); + return -EINVAL; + } + + ac_config.io_type = FH_AC_MIC_IN; + ac_config.period_size = DEFAULT_PERIOD_SIZE; + ac_config.volume = DEFAULT_VOLUME; + +#ifdef USING_HARD_STREAM_AUDIO + int ret = 0; + FH_AC_NR_CONFIG nr_cfg; + nr_cfg.enable = 1; + nr_cfg.level = 1; + + if (ret = FH_AC_Set_Config(&ac_config)) { + KVS_LOG("FH_AC_Set_Config failed: %d\n", ret); + } else if (ret = FH_AC_AI_MICIN_SetVol(DEFAULT_MICIN_VOL)) { // level-1 gain for mic-in + KVS_LOG("FH_AC_Set_Config failed: %d\n", ret); + } else if (ret = FH_AC_AI_SetVol(DEFAULT_AIN_VOL)) { // level-2 gain + KVS_LOG("FH_AC_AI_SetVol failed: %d\n", ret); + } else if (ret = FH_AC_NR_SetConfig(&nr_cfg)) { + KVS_LOG("FH_AC_NR_SetConfig failed: %d\n", ret); + } + + if (ret) { +#ifdef USING_HARD_STREAM_AUDIO + FH_AC_AI_Disable(); + if (format == AUD_FMT_AAC) { + if (audioHandle->aacHandle) { + fh_aacenc_destroy(audioHandle->aacHandle); + } + audioHandle->aacHandle = fh_aacenc_create(1, ac_config.sample_rate, 0); + } +#endif + return -EAGAIN; + } + +#endif + + audioHandle->format = format; + audioHandle->channel = channel; + audioHandle->sampleRate = sampleRate; + audioHandle->bitDepth = bitDepth; + + return 0; +} + +int audioCapturerGetFormat(const AudioCapturerHandle const handle, AudioFormat* pFormat, AudioChannel* pChannel, AudioSampleRate* pSampleRate, + AudioBitDepth* pBitDepth) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + *pFormat = audioHandle->format; + *pChannel = audioHandle->channel; + *pSampleRate = audioHandle->sampleRate; + *pBitDepth = audioHandle->bitDepth; + + return 0; +} + +int audioCapturerAcquireStream(AudioCapturerHandle handle) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + +#ifdef USING_HARD_STREAM_AUDIO + if (FH_AC_AI_Enable()) { + KVS_LOG("Audio device disable failed"); + return -EAGAIN; + } +#endif + + return setStatus(handle, AUD_CAP_STATUS_STREAM_ON); +} + +int audioCapturerGetFrame(AudioCapturerHandle handle, void* pFrameDataBuffer, const size_t frameDataBufferSize, uint64_t* pTimestamp, + size_t* pFrameSize) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + HANDLE_STATUS_CHECK(audioHandle, AUD_CAP_STATUS_STREAM_ON); + + if (!pFrameDataBuffer || !pTimestamp || !pFrameSize) { + return -EINVAL; + } + +#ifdef USING_HARD_STREAM_AUDIO + int ret = 0; + short frame_buf[1024]; + uint64_t audio_pts; + FH_AC_FRAME_S audio_frame; + + audio_frame.data = (FH_UINT8*) frame_buf; + ret = FH_AC_AI_GetFrameWithPts(&audio_frame, &audio_pts); + if (ret || !audio_frame.len) { + KVS_LOG("FH_AC_AI_GetFrame failed"); + return -EAGAIN; + } + + // Encode frame + if (audioHandle->format != AUD_FMT_PCM) { + int convert_num; + unsigned char* convert_raw_addr = NULL; + unsigned char* convert_buf = malloc(audio_frame.len >> 1); + if (!convert_buf) { + KVS_LOG("oom\n"); + return -EAGAIN; + } + + switch (audioHandle->format) { + case AUD_FMT_G711A: + convert_num = fh_pcm_2_g711A((unsigned char*) audio_frame.data, audio_frame.len, (unsigned char*) convert_buf); + convert_raw_addr = convert_buf; + break; + + case AUD_FMT_AAC: + convert_num = fh_aacenc_encode(audioHandle->aacHandle, (unsigned char*) audio_frame.data, audio_frame.len, + (unsigned char*) convert_buf, (audio_frame.len >> 2)); + convert_num -= 7; // offset aac header, kvs not support + convert_raw_addr = convert_buf + 7; + break; + + default: + KVS_LOG("Unsupported format %d", audioHandle->format); + break; + } + + if (frameDataBufferSize >= convert_num) { + memcpy(pFrameDataBuffer, (void*) convert_raw_addr, convert_num); + *pFrameSize = convert_num; + *pTimestamp = audio_pts; + free(convert_buf); + } else { + KVS_LOG("FrameDataBufferSize(%d) < frameSize(%d), frame dropped", frameDataBufferSize, convert_num); + *pFrameSize = 0; + ret = -ENOMEM; + free(convert_buf); + } + } else { + memcpy(pFrameDataBuffer, (void*) audio_frame.data, audio_frame.len); + *pFrameSize = audio_frame.len; + *pTimestamp = audio_pts; + } + + return ret; +#else + return 0; +#endif +} + +int audioCapturerReleaseStream(AudioCapturerHandle handle) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + +#ifdef USING_HARD_STREAM_AUDIO + if (FH_AC_AI_Disable()) { + KVS_LOG("Audio device disable failed"); + return -EAGAIN; + } +#endif + + return setStatus(handle, AUD_CAP_STATUS_STREAM_OFF); +} + +void audioCapturerDestory(AudioCapturerHandle handle) +{ + if (!handle) { + return; + } + HANDLE_GET(handle); + +#ifdef USING_HARD_STREAM_AUDIO + if (audioHandle->status == AUD_CAP_STATUS_STREAM_ON) { + FH_AC_AI_Disable(); + } + + FH_AC_DeInit(); + + if (audioHandle->aacHandle) + fh_aacenc_destroy(audioHandle->aacHandle); +#endif + + setStatus(handle, AUD_CAP_STATUS_NOT_READY); + + free(handle); +} diff --git a/source/FH8626V100/FH8626V100AudioPlayer.c b/source/FH8626V100/FH8626V100AudioPlayer.c new file mode 100644 index 0000000..5f99275 --- /dev/null +++ b/source/FH8626V100/FH8626V100AudioPlayer.c @@ -0,0 +1,74 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include + +#include "com/amazonaws/kinesis/video/player/AudioPlayer.h" + +#define FH8626_HANDLE_GET(x) FH8626AudioPlayer* fh8626Handle = (FH8626AudioPlayer*) ((x)) + +typedef struct { + AudioPlayerStatus status; + AudioCapability capability; + AudioFormat format; + AudioChannel channel; + AudioBitDepth bitDepth; + AudioSampleRate sampleRate; +} FH8626AudioPlayer; + +AudioPlayerHandle audioPlayerCreate(void) +{ + return NULL; +} + +AudioPlayerStatus audioPlayerGetStatus(const AudioPlayerHandle const handle) +{ + return AUD_PLY_STATUS_NOT_READY; +} + +int audioPlayerGetCapability(const AudioPlayerHandle const handle, AudioCapability* pCapability) +{ + return -EAGAIN; +} + +int audioPlayerSetFormat(AudioPlayerHandle handle, const AudioFormat format, const AudioChannel channel, const AudioSampleRate sampleRate, + const AudioBitDepth bitDepth) +{ + return -EAGAIN; +} + +int audioPlayerGetFormat(const AudioPlayerHandle const handle, AudioFormat* pFormat, AudioChannel* pChannel, AudioSampleRate* pSampleRate, + AudioBitDepth* pBitDepth) +{ + return -EAGAIN; +} + +int audioPlayerAcquireStream(AudioPlayerHandle handle) +{ + return -EAGAIN; +} + +int audioPlayerWriteFrame(AudioPlayerHandle handle, void* pData, const size_t size) +{ + return -EAGAIN; +} + +int audioPlayerReleaseStream(AudioPlayerHandle handle) +{ + return -EAGAIN; +} + +void audioPlayerDestory(AudioPlayerHandle handle) +{ +} diff --git a/source/FH8626V100/FH8626V100Common.h b/source/FH8626V100/FH8626V100Common.h new file mode 100644 index 0000000..e700f07 --- /dev/null +++ b/source/FH8626V100/FH8626V100Common.h @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#define ATOMIC_INT volatile size_t +#define ATOMIC_INT_ADD(x) __sync_add_and_fetch((x), 1) +#define ATOMIC_INT_SUB(x) __sync_sub_and_fetch((x), 1) + +#define ATOMIC_BOOL volatile bool +#define ATOMIC_BOOL_TRUE(x) __sync_or_and_fetch((x), TRUE) +#define ATOMIC_BOOL_FALSE(x) __sync_and_and_fetch((x), FALSE) + +#define MUTEX pthread_mutex_t +#define MUTEX_CREATE(x) pthread_mutex_init((x), NULL) +#define MUTEX_LOCK(x) pthread_mutex_lock((x)) +#define MUTEX_UNLOCK(x) pthread_mutex_unlock((x)) +#define MUTEX_DESTORY(x) pthread_mutex_destroy((x)) + +#define THREAD pthread_t +#define THREAD_CREATE(thread, threadFunc, args) pthread_create((thread), NULL, (threadFunc), (args)) +#define THREAD_JOIN(thread, threadReturn) pthread_join((thread), (threadReturn)) + +#define KVS_DEBUG_LOG +#ifndef KVS_DEBUG_LOG +#define KVS_LOG(format, args...) +#else +#define KVS_LOG(format, args...) printf("[kvs %s:%d] " format, __func__, __LINE__, ##args) +#endif + +#define HANDLE_NULL_CHECK(x) \ + if (!(x)) { \ + KVS_LOG("HANDLE_STATUS_CHECK err\n"); \ + return -EINVAL; \ + } +#define HANDLE_STATUS_CHECK(FH8626Handle, expectedStatus) \ + if ((FH8626Handle)->status != (expectedStatus)) { \ + KVS_LOG("HANDLE_STATUS_CHECK err\n"); \ + return -EAGAIN; \ + } diff --git a/source/FH8626V100/FH8626V100VideoCapturer.c b/source/FH8626V100/FH8626V100VideoCapturer.c new file mode 100644 index 0000000..4ac91b9 --- /dev/null +++ b/source/FH8626V100/FH8626V100VideoCapturer.c @@ -0,0 +1,293 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include +#include + +#include "com/amazonaws/kinesis/video/capturer/VideoCapturer.h" + +#include "FH8626V100Common.h" +#include "sample_common.h" + +#define USING_HARD_STREAM_VIDEO + +#define HANDLE_GET(x) FH8626V100VideoCapturer* videoHandle = (FH8626V100VideoCapturer*) ((x)) + +#define MAIN_CHN 0 + +typedef struct { + VideoCapturerStatus status; + VideoCapability capability; + VideoFormat format; + VideoResolution resolution; + uint8_t channelNum; +} FH8626V100VideoCapturer; + +static int setStatus(VideoCapturerHandle handle, const VideoCapturerStatus newStatus) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + if (newStatus != videoHandle->status) { + videoHandle->status = newStatus; + KVS_LOG("VideoCapturer set new status[%d]\n", newStatus); + } + + return 0; +} + +static int startRecvPic(VideoCapturerHandle handle, uint8_t chnNum) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + HANDLE_STATUS_CHECK(videoHandle, VID_CAP_STATUS_STREAM_ON); + +#ifdef USING_HARD_STREAM_VIDEO + if (videoHandle->format == VID_FMT_H264) { + if (FH_VENC_StartRecvPic(chnNum)) { + KVS_LOG("FH_VENC_StartRecvPic(%d) failed\n", chnNum); + return -EAGAIN; + } + + } else { + KVS_LOG("err todo\n"); + return -EAGAIN; + } +#endif + + return 0; +} + +static int stopRecvPic(VideoCapturerHandle handle, uint8_t chnNum) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + +#ifdef USING_HARD_STREAM_VIDEO + if (videoHandle->format == VID_FMT_H264) { + if (FH_VENC_StopRecvPic(chnNum)) { + KVS_LOG("FH_VENC_StopRecvPic(%d) failed\n", chnNum); + return -EAGAIN; + } + } else { + KVS_LOG("err todo\n"); + return -EAGAIN; + } +#endif + return 0; +} + +VideoCapturerHandle videoCapturerCreate(void) +{ + FH8626V100VideoCapturer* videoHandle = NULL; + + if (!(videoHandle = (FH8626V100VideoCapturer*) malloc(sizeof(FH8626V100VideoCapturer)))) { + KVS_LOG("videoHandle malloc failed\n"); + return NULL; + } + + memset(videoHandle, 0, sizeof(FH8626V100VideoCapturer)); + +#ifdef USING_HARD_STREAM_VIDEO + if (sample_video_init()) { + KVS_LOG("video init failed\n"); + free(videoHandle); + return NULL; + } + + FH_VENC_StopRecvPic(videoHandle->channelNum); +#endif + + // Now implementation supports H.264, RAW(NV12), 1080p, 720p, 480p, 360p and 320p + videoHandle->capability.formats = (1 << (VID_FMT_H264 - 1)); + videoHandle->capability.resolutions = (1 << (VID_RES_1080P - 1)); + + setStatus((VideoCapturerHandle) videoHandle, VID_CAP_STATUS_STREAM_OFF); + + return (VideoCapturerHandle) videoHandle; +} + +VideoCapturerStatus videoCapturerGetStatus(const VideoCapturerHandle const handle) +{ + if (!handle) { + return VID_CAP_STATUS_NOT_READY; + } + + HANDLE_GET(handle); + + return videoHandle->status; +} + +int videoCapturerGetCapability(const VideoCapturerHandle const handle, VideoCapability* pCapability) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + if (!pCapability) { + return -EAGAIN; + } + + *pCapability = videoHandle->capability; + + return 0; +} + +int videoCapturerSetFormat(VideoCapturerHandle handle, const VideoFormat format, const VideoResolution resolution) +{ + int ret; + int fmt, chn, res; + + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + switch (resolution) { + case VID_RES_1080P: + chn = MAIN_CHN; + videoHandle->channelNum = chn; + res = FORMAT_1080P15; + break; + default: + KVS_LOG("Unsupported resolution %d", resolution); + return -EINVAL; + } + + switch (format) { + case VID_FMT_H264: + fmt = FH_NORMAL_H264; + break; + default: + KVS_LOG("Unsupported format %d", format); + return -EINVAL; + } + +#ifdef USING_HARD_STREAM_VIDEO + if (change_video(chn, fmt, res)) { + KVS_LOG("change video failed\n"); + return -EINVAL; + } +#endif + + videoHandle->format = format; + videoHandle->resolution = resolution; + + return 0; +} + +int videoCapturerGetFormat(const VideoCapturerHandle const handle, VideoFormat* pFormat, VideoResolution* pResolution) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + *pFormat = videoHandle->format; + *pResolution = videoHandle->resolution; + + return 0; +} + +int videoCapturerAcquireStream(VideoCapturerHandle handle) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + setStatus(handle, VID_CAP_STATUS_STREAM_ON); + + return startRecvPic(handle, videoHandle->channelNum); +} + +int videoCapturerGetFrame(VideoCapturerHandle handle, void* pFrameDataBuffer, const size_t frameDataBufferSize, uint64_t* pTimestamp, + size_t* pFrameSize) +{ + int i, ret; + int frmlen, offset; + FH_VENC_STREAM stream; + + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + HANDLE_STATUS_CHECK(videoHandle, VID_CAP_STATUS_STREAM_ON); + + if (!pFrameDataBuffer || !pTimestamp || !pFrameSize) { + KVS_LOG("param err\n"); + return -EINVAL; + } + +#ifdef USING_HARD_STREAM_VIDEO + if (videoHandle->format == VID_FMT_RAW) { + KVS_LOG("TODO VID_FMT_RAW"); + return -EINVAL; + } else if (videoHandle->format == VID_FMT_H264) { + ret = FH_VENC_GetStream_Block(FH_STREAM_H264, &stream); + if (ret == RETURN_OK) { + frmlen = 0; + for (i = 0; i < stream.h264_stream.nalu_cnt; i++) { + frmlen += stream.h264_stream.nalu[i].length; + } + + if (frameDataBufferSize >= frmlen) { + *pFrameSize = frmlen; + *pTimestamp = stream.h264_stream.time_stamp; + + offset = 0; + for (i = 0; i < stream.h264_stream.nalu_cnt; i++) { + memcpy(pFrameDataBuffer + offset, stream.h264_stream.nalu[i].start, stream.h264_stream.nalu[i].length); + offset += stream.h264_stream.nalu[i].length; + } + } else { + KVS_LOG("FrameDataBufferSize(%d) < frameSize(%d), frame dropped", frameDataBufferSize, frmlen); + *pFrameSize = 0; + ret = -ENOMEM; + } + + FH_VENC_ReleaseStream(videoHandle->channelNum); + } else { + KVS_LOG("FH_VENC_GetStream_Block failed, %x\n", ret); + } + } else { + KVS_LOG("format not support"); + return -EINVAL; + } +#endif + + return 0; +} + +int videoCapturerReleaseStream(VideoCapturerHandle handle) +{ + HANDLE_NULL_CHECK(handle); + HANDLE_GET(handle); + + stopRecvPic(handle, videoHandle->channelNum); + + return setStatus(handle, VID_CAP_STATUS_STREAM_OFF); +} + +void videoCapturerDestory(VideoCapturerHandle handle) +{ + if (!handle) { + return; + } + HANDLE_GET(handle); + +#ifdef USING_HARD_STREAM_VIDEO + if (videoHandle->status == VID_CAP_STATUS_STREAM_ON) { + videoCapturerReleaseStream(handle); + } + + sample_video_exit(); +#endif + + setStatus(handle, VID_CAP_STATUS_NOT_READY); + free(handle); +}