diff --git a/3rdparty/C610/C610GstCmd.h b/3rdparty/C610/C610GstCmd.h new file mode 100755 index 0000000..81b03c7 --- /dev/null +++ b/3rdparty/C610/C610GstCmd.h @@ -0,0 +1,35 @@ +/* + * Copyright 2023 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 + +// camera0: +// gst-launch-1.0 -e qtiqmmfsrc name=camsrc ! video/x-raw\(memory:GBM\),format=NV12,width=1920,height=1080,framerate=25/1 ! queue ! omxh264enc control-rate=max-bitrate target-bitrate=6000000 interval-intraframes=24 periodicity-idr=1 ! queue ! h264parse config-interval=1 ! mpegtsmux name=muxer ! tee name=t ! queue ! tcpserversink host=127.0.0.1 port=8501 t. ! queue ! tcpserversink host=127.0.0.1 port=8901 +// "tcpclientsrc host=127.0.0.1 port=8501 ! tsdemux name=muxer ! h264parse ! appsink name=sink" +// camera1: +// gst-launch-1.0 -e qtiqmmfsrc name=camsrc camera=1 ! video/x-raw\(memory:GBM\),format=NV12,width=1920,height=1080,framerate=25/1 ! queue ! omxh264enc control-rate=max-bitrate target-bitrate=6000000 interval-intraframes=24 periodicity-idr=1 ! queue ! h264parse config-interval=1 ! mpegtsmux name=muxer ! tee name=t ! queue ! tcpserversink host=127.0.0.1 port=8502 t. ! queue ! tcpserversink host=127.0.0.1 port=8902 +// "tcpclientsrc host=127.0.0.1 port=8502 ! tsdemux name=muxer ! h264parse ! appsink name=sink" +#define GST_LAUNCH_VIDEO_PIPELINE_CMD \ + "tcpclientsrc host=%s port=%s ! tsdemux name=muxer ! h264parse ! appsink name=%s" + +// audio: +// gst-launch-1.0 pulsesrc volume=100 ! capsfilter caps=audio/x-raw,format=S16LE,rate=8000,channels=2 ! audioconvert ! capsfilter caps=audio/x-raw,format=S16LE,rate=8000,channels=1 ! alawenc ! tee name=t ! queue ! tcpserversink host=127.0.0.1 port=8503 t. ! queue ! tcpserversink host=127.0.0.1 port=8903 +// "tcpclientsrc host=127.0.0.1 port=8503 ! appsink name=sink" +#define GST_LAUNCH_AUDIO_PIPELINE_CMD \ + "tcpclientsrc host=%s port=%s ! appsink name=%s" + +// audio player: +// "appsrc name=alawsrc ! capsfilter caps=audio/x-alaw,rate=8000,channels=1 ! alawdec ! queue ! pulsesink volume=1" +#define GST_LAUNCH_AUDIO_PLAYER_PIPELINE_CMD \ + "appsrc name=%s ! capsfilter caps=audio/x-alaw,rate=8000,channels=1 ! alawdec ! queue ! pulsesink volume=1" diff --git a/3rdparty/C610/OEToolchainConfig.cmake b/3rdparty/C610/OEToolchainConfig.cmake new file mode 100755 index 0000000..a7c67de --- /dev/null +++ b/3rdparty/C610/OEToolchainConfig.cmake @@ -0,0 +1,24 @@ +set( CMAKE_SYSTEM_NAME Linux ) +set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{CFLAGS}" CACHE STRING "" FORCE ) +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{CXXFLAGS}" CACHE STRING "" FORCE ) +set( CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS} CACHE STRING "" FORCE ) +set( CMAKE_SYSROOT $ENV{OECORE_TARGET_SYSROOT} ) + +set( CMAKE_FIND_ROOT_PATH $ENV{OECORE_TARGET_SYSROOT} ) +set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) +set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH ) +set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH ) +set( CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH ) + +set(CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX "$ENV{OE_CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX}") + +# Set CMAKE_SYSTEM_PROCESSOR from the sysroot name (assuming processor-distro-os). +if ($ENV{SDKTARGETSYSROOT} MATCHES "/sysroots/([a-zA-Z0-9_-]+)-.+-.+") + set(CMAKE_SYSTEM_PROCESSOR ${CMAKE_MATCH_1}) +endif() + +# Include the toolchain configuration subscripts +file( GLOB toolchain_config_files "${CMAKE_TOOLCHAIN_FILE}.d/*.cmake" ) +foreach(config ${toolchain_config_files}) + include(${config}) +endforeach() diff --git a/3rdparty/GSTREAMER/GSTREAMERGstCmd.h b/3rdparty/GSTREAMER/GSTREAMERGstCmd.h new file mode 100755 index 0000000..4620ead --- /dev/null +++ b/3rdparty/GSTREAMER/GSTREAMERGstCmd.h @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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 + +// video: +// gst-launch-1.0 -e rtspsrc location=rtsp://192.168.0.60:554/stream0 latency=500 ! rtph264depay ! queue ! h264parse config-interval=1 ! mpegtsmux name=muxer ! tcpserversink host=127.0.0.1 port=8501 & +// "tcpclientsrc host=127.0.0.1 port=8502 ! tsdemux name=muxer ! h264parse ! appsink name=sink" +#define GST_LAUNCH_VIDEO_PIPELINE_CMD \ + "tcpclientsrc host=%s port=%s ! tsdemux name=muxer ! h264parse ! appsink name=%s" + +// audio: +// gst-launch-1.0 -e rtspsrc location=rtsp://192.168.0.60:554/stream0 latency=500 ! rtppcmadepay ! queue ! tcpserversink host=127.0.0.1 port=8503 & +// "tcpclientsrc host=127.0.0.1 port=8503 ! appsink name=sink" +#define GST_LAUNCH_AUDIO_PIPELINE_CMD \ + "tcpclientsrc host=%s port=%s ! appsink name=%s" + +// audio player: +// "appsrc name=alawsrc ! capsfilter caps=audio/x-alaw,rate=8000,channels=1 ! alawdec ! autoaudiosink" +#define GST_LAUNCH_AUDIO_PLAYER_PIPELINE_CMD \ + "appsrc name=%s ! capsfilter caps=audio/x-alaw,rate=8000,channels=1 ! alawdec ! autoaudiosink" diff --git a/3rdparty/GSTREAMER/GSTREAMERPort.c b/3rdparty/GSTREAMER/GSTREAMERPort.c new file mode 100755 index 0000000..72d92ce --- /dev/null +++ b/3rdparty/GSTREAMER/GSTREAMERPort.c @@ -0,0 +1,325 @@ +/* + * Copyright 2023 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 "GSTREAMERPort.h" + +#define FILEEXTENSION GstCmd.h +#define CONCATENATE_DETAIL(x, y) x##y +#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y) +#define STRINGIFY_DETAIL(x) #x +#define STRINGIFY(x) STRINGIFY_DETAIL(x) +#define INCLUDE_FILE(name, tail) STRINGIFY(CONCATENATE(name, tail)) +#include INCLUDE_FILE(BOARD, FILEEXTENSION) + +#define GST_TCP_HOST "127.0.0.1" +#define GST_VIDEO_TCP_PORT "8501" +#define GST_AUDIO_TCP_PORT "8503" +#define GST_SIGNAL_CALLBACK_NEW_SAMPLE "new-sample" +#define GST_SIGNAL_CALLBACK_MSG_ERROR "message::error" +#define GST_SIGNAL_CALLBACK_MSG_EOS "message::eos" + +typedef enum { + VIDEO_CAPTURE_APP_SINK, + AUDIO_CAPTURE_APP_SINK, + AUDIO_PLAYER_APP_SRC +} GSTAppModuleType; + +static GstElement *g_pipeline[3]; // 0: videoCapture; 1: audioCapture; 2: audioPlayer +static int g_gstInit = 0; + +/** + * @brief the callback is invoked when the error happens on the bus. + * + * @param[in] bus the bus of the callback. + * @param[in] msg the msg of the callback. + * @param[in] udata the user data. + * + * @return STATUS code of the execution. STATUS_SUCCESS on success + */ +static void onMsgErrorFromBus(GstBus* bus, GstMessage* msg, gpointer* udata) +{ + GError* err; + gchar* debug_info; + + if ((bus == NULL) || (msg == NULL)) { + goto CleanUp; + } + + gst_message_parse_error(msg, &err, &debug_info); + if (err != NULL) { + printf("error code: %d\n", err->code); + printf("error received from element %s: %s\n", gst_object_get_name(msg->src), err->message); + } + if (debug_info != NULL) { + printf("debugging information: %s\n", debug_info); + } + +CleanUp: + g_error_free(err); + g_free(debug_info); + + return; +} + +/** + * @brief the callback is invoked when the end of stream happens on the bus. + * + * @param[in] bus the bus of the callback. + * @param[in] msg the msg of the callback. + * @param[in] udata the user data. + * + * @return STATUS code of the execution. STATUS_SUCCESS on success + */ +static void onMsgEosFromBus(GstBus* bus, GstMessage* msg, gpointer* udata) +{ + if ((bus == NULL) || (msg == NULL)) { + goto CleanUp; + } + printf("msg EOS\n"); + +CleanUp: + + return; +} + +static int initGstAppSinkSrc(GSTAppSinkSrc *handle, GSTAppModuleType type) +{ + GstElement *pipeline = NULL; + GstBus *bus = NULL; + GstElement *src = NULL, *sink = NULL; + char *port = NULL; + char cmd[512] = { 0 }; + + if (!g_gstInit) { + gst_init(NULL, NULL); + g_gstInit = 1; + } + + switch (type) { + case VIDEO_CAPTURE_APP_SINK: + port = getenv("VIDEO_PORT"); + port = (NULL != port) ? port : GST_VIDEO_TCP_PORT; + snprintf(cmd, sizeof(cmd), GST_LAUNCH_VIDEO_PIPELINE_CMD, GST_TCP_HOST, port, GST_VIDEO_SINK_NAME); + g_pipeline[VIDEO_CAPTURE_PIPELINE] = gst_parse_launch(cmd, NULL); + pipeline = g_pipeline[VIDEO_CAPTURE_PIPELINE]; + sink = gst_bin_get_by_name(GST_BIN(pipeline), GST_VIDEO_SINK_NAME); + break; + case AUDIO_CAPTURE_APP_SINK: + port = getenv("AUDIO_PORT"); + port = (NULL != port) ? port : GST_AUDIO_TCP_PORT; + snprintf(cmd, sizeof(cmd), GST_LAUNCH_AUDIO_PIPELINE_CMD, GST_TCP_HOST, port, GST_AUDIO_SINK_NAME); + g_pipeline[AUDIO_CAPTURE_PIPELINE] = gst_parse_launch(cmd, NULL); + pipeline = g_pipeline[AUDIO_CAPTURE_PIPELINE]; + sink = gst_bin_get_by_name(GST_BIN(pipeline), GST_AUDIO_SINK_NAME); + break; + case AUDIO_PLAYER_APP_SRC: + snprintf(cmd, sizeof(cmd), GST_LAUNCH_AUDIO_PLAYER_PIPELINE_CMD, GST_AUDIO_SRC_NAME); + g_pipeline[AUDIO_PLAYER_PIPELINE] = gst_parse_launch(cmd, NULL); + pipeline = g_pipeline[AUDIO_PLAYER_PIPELINE]; + src = gst_bin_get_by_name(GST_BIN(pipeline), GST_AUDIO_SRC_NAME); + break; + default: + break; + } + if (NULL == pipeline || (NULL == src && NULL == sink)) { + printf("pipeline or src or sink is NULL!\n"); + return -1; + } + + // Instruct the bus to emit signals for each received message, and connect to the interesting signals + if ((bus = gst_element_get_bus(pipeline)) != NULL) { + gst_bus_add_signal_watch(bus); + g_signal_connect(bus, GST_SIGNAL_CALLBACK_MSG_ERROR, G_CALLBACK(onMsgErrorFromBus), NULL); + g_signal_connect(bus, GST_SIGNAL_CALLBACK_MSG_EOS, G_CALLBACK(onMsgEosFromBus), NULL); + } + + if (NULL != src) { + g_object_set(src, "is-live", TRUE, NULL); + } + + return 0; +} + +static int startGstAppSinkSrc(GSTAppModuleType type) +{ + GstElement *pipeline = NULL; + switch (type) { + case VIDEO_CAPTURE_APP_SINK: + pipeline = g_pipeline[VIDEO_CAPTURE_PIPELINE]; + break; + case AUDIO_CAPTURE_APP_SINK: + pipeline = g_pipeline[AUDIO_CAPTURE_PIPELINE]; + break; + case AUDIO_PLAYER_APP_SRC: + pipeline = g_pipeline[AUDIO_PLAYER_PIPELINE]; + break; + default: + break; + } + + if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(pipeline, GST_STATE_PLAYING)) { + printf("gst_element_set_state failed!\n"); + return -1; + } + + return 0; +} + +static int stopGstAppSinkSrc(GSTAppModuleType type) +{ + GstElement *pipeline = NULL; + switch (type) { + case VIDEO_CAPTURE_APP_SINK: + pipeline = g_pipeline[VIDEO_CAPTURE_PIPELINE]; + break; + case AUDIO_CAPTURE_APP_SINK: + pipeline = g_pipeline[AUDIO_CAPTURE_PIPELINE]; + break; + case AUDIO_PLAYER_APP_SRC: + pipeline = g_pipeline[AUDIO_PLAYER_PIPELINE]; + break; + default: + break; + } + + if (GST_STATE_CHANGE_FAILURE == gst_element_set_state(pipeline, GST_STATE_NULL)) { + printf("gst_element_set_state failed!\n"); + return -1; + } + + return 0; +} + +static int deinitGstAppSinkSrc(GSTAppModuleType type) +{ + GstElement *pipeline = NULL; + switch (type) { + case VIDEO_CAPTURE_APP_SINK: + pipeline = g_pipeline[VIDEO_CAPTURE_PIPELINE]; + break; + case AUDIO_CAPTURE_APP_SINK: + pipeline = g_pipeline[AUDIO_CAPTURE_PIPELINE]; + break; + case AUDIO_PLAYER_APP_SRC: + pipeline = g_pipeline[AUDIO_PLAYER_PIPELINE]; + break; + default: + break; + } + + gst_object_unref(pipeline); + + return 0; +} + +int initGstVideoSrc(GSTAppSinkSrc *handle) +{ + return initGstAppSinkSrc(handle, VIDEO_CAPTURE_APP_SINK); +} + +int startGstVideoSrc(void) +{ + return startGstAppSinkSrc(VIDEO_CAPTURE_APP_SINK); +} + +int stopGstVideoSrc(void) +{ + return stopGstAppSinkSrc(VIDEO_CAPTURE_APP_SINK); +} + +int deinitGstVideoSrc(void) +{ + return deinitGstAppSinkSrc(VIDEO_CAPTURE_APP_SINK); +} + +int initGstAudioSrc(GSTAppSinkSrc *handle) +{ + return initGstAppSinkSrc(handle, AUDIO_CAPTURE_APP_SINK); +} + +int startGstAudioSrc(void) +{ + return startGstAppSinkSrc(AUDIO_CAPTURE_APP_SINK); +} + +int stopGstAudioSrc(void) +{ + return stopGstAppSinkSrc(AUDIO_CAPTURE_APP_SINK); +} + +int deinitGstAudioSrc(void) +{ + return deinitGstAppSinkSrc(AUDIO_CAPTURE_APP_SINK); +} + +int initGstAudioPlayer(GSTAppSinkSrc *handle) +{ + return initGstAppSinkSrc(handle, AUDIO_PLAYER_APP_SRC); +} + +int startGstAudioPlayer(void) +{ + return startGstAppSinkSrc(AUDIO_PLAYER_APP_SRC); +} + +int stopGstAudioPlayer(void) +{ + return stopGstAppSinkSrc(AUDIO_PLAYER_APP_SRC); +} + +int deinitGstAudioPlayer(void) +{ + return deinitGstAppSinkSrc(AUDIO_PLAYER_APP_SRC); +} + +int writeToGstAudioPlayer(uint8_t *pData, size_t size) +{ + GstFlowReturn ret; + GstElement *pipeline = g_pipeline[AUDIO_PLAYER_PIPELINE]; + GstElement *src = gst_bin_get_by_name(GST_BIN(pipeline), GST_AUDIO_SRC_NAME); + + GstBuffer *buffer = gst_buffer_new_and_alloc(size); + if (buffer) { + gst_buffer_fill(buffer, 0, pData, size); + g_signal_emit_by_name(src, "push-buffer", buffer, &ret); + gst_buffer_unref(buffer); + } + + if (ret != GST_FLOW_OK) { + printf("Error pushing buffer to appsrc\n"); + return -1; + } + + return 0; +} + +__attribute__((weak)) uint64_t getEpochTimestampInUs(void) +{ + uint64_t timestamp = 0; + + struct timeval tv; + gettimeofday(&tv, NULL); + timestamp = (uint64_t) (tv.tv_sec) * 1000 * 1000 + (uint64_t) (tv.tv_usec); + + return timestamp; +} + +GstElement * getPipeline(int type) +{ + return g_pipeline[type]; +} \ No newline at end of file diff --git a/3rdparty/GSTREAMER/GSTREAMERPort.h b/3rdparty/GSTREAMER/GSTREAMERPort.h new file mode 100755 index 0000000..7a17ae6 --- /dev/null +++ b/3rdparty/GSTREAMER/GSTREAMERPort.h @@ -0,0 +1,53 @@ +/* + * Copyright 2023 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 +#include + +#define GST_VIDEO_SINK_NAME "h264sink" +#define GST_AUDIO_SINK_NAME "alawsink" +#define GST_AUDIO_SRC_NAME "alawsrc" + +typedef struct { + void *capturerHandle; +} GSTAppSinkSrc; + +typedef enum { + VIDEO_CAPTURE_PIPELINE, + AUDIO_CAPTURE_PIPELINE, + AUDIO_PLAYER_PIPELINE +} GstPipelineType; + +int initGstVideoSrc(GSTAppSinkSrc *handle); +int startGstVideoSrc(void); +int stopGstVideoSrc(void); +int deinitGstVideoSrc(void); + +int initGstAudioSrc(GSTAppSinkSrc *handle); +int startGstAudioSrc(void); +int stopGstAudioSrc(void); +int deinitGstAudioSrc(void); + +int initGstAudioPlayer(GSTAppSinkSrc *handle); +int startGstAudioPlayer(void); +int stopGstAudioPlayer(void); +int deinitGstAudioPlayer(void); +int writeToGstAudioPlayer(uint8_t *pData, size_t size); + +uint64_t getEpochTimestampInUs(void); +GstElement * getPipeline(int type); diff --git a/3rdparty/README.md b/3rdparty/README.md old mode 100644 new mode 100755 index a5c10b9..8a169d8 --- a/3rdparty/README.md +++ b/3rdparty/README.md @@ -4,6 +4,8 @@ This documents provides guidelines to install board SDK required by this library - [Board SDK Install Guide](#board-sdk-install-guide) - [FILE](#file) + - [C610](#c610) + - [GSTREAMER](#Gstreamer) - [V4L2](#v4l2) - [T31](#t31) - [FH8626V100](#fh8626v100) @@ -15,6 +17,22 @@ This documents provides guidelines to install board SDK required by this library FILE capturer is a dummy capturer that read sample frames from [resources/frames/]. It doesn't require user to manually install any 3rdparty SDK. +## C610 + +C610 receive and retrieve audio and video from the device through the Gstreamer pipeline. It doesn't require user to manually install any 3rdparty SDK. + +## Gstreamer + +Gstreamer capturer receive and retrieve audio and video from the device through the Gstreamer pipeline. It require user to manually install Gstreamer library. You may install it via: +``` +sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base-apps gstreamer1.0-plugins-bad gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-tools +``` +You should provide H264 video frames and g711a audio frames to the pipeline by Tcp. The pipeline used in the code is defined as the following macro. +``` +GST_LAUNCH_VIDEO_PIPELINE_CMD +GST_LAUNCH_AUDIO_PIPELINE_CMD +GST_LAUNCH_AUDIO_PLAYER_PIPELINE_CMD +``` ## V4L2 V4L2 request libv4l2 on your build device. You may install it via: diff --git a/CMake/C610.cmake b/CMake/C610.cmake new file mode 100755 index 0000000..a91dd51 --- /dev/null +++ b/CMake/C610.cmake @@ -0,0 +1,21 @@ +if(BOARD STREQUAL "C610") + set(BOARD_SDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/${BOARD}) + + set(BOARD_SRCS + ${BOARD_SDK_DIR}/../GSTREAMER/GSTREAMERPort.c + ) + set(BOARD_INCS_DIR + ${BOARD_SDK_DIR} + ${BOARD_SDK_DIR}/../GSTREAMER/ + ) + set(BOARD_LIBS_DIR + ) + set(BOARD_DESTINATION_PLATFORM + aarch64-oe-linux + ) + set(BOARD_LIBS_SHARED + ) + set(BOARD_LIBS_STATIC + ) + add_definitions(-DBOARD=${BOARD}) +endif() diff --git a/CMake/GSTREAMER.cmake b/CMake/GSTREAMER.cmake new file mode 100755 index 0000000..862d85c --- /dev/null +++ b/CMake/GSTREAMER.cmake @@ -0,0 +1,17 @@ +if(BOARD STREQUAL "GSTREAMER") + set(BOARD_SDK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/${BOARD}) + + set(BOARD_SRCS + ${BOARD_SDK_DIR}/GSTREAMERPort.c + ) + set(BOARD_INCS_DIR + ${BOARD_SDK_DIR} + ) + set(BOARD_LIBS_DIR + ) + set(BOARD_LIBS_SHARED + ) + set(BOARD_LIBS_STATIC + ) + add_definitions(-DBOARD=${BOARD}) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 index 69d753e..7c3bfcb --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) option(USE_MUCLIBC "Use muclibc" OFF) +set(SRCBOARD ${BOARD}) # Board Specific Parts Start if(NOT BOARD OR BOARD STREQUAL "") @@ -17,6 +18,11 @@ elseif(BOARD STREQUAL "FILE") message(STATUS "Selected board FILE") elseif(BOARD STREQUAL "V4L2") message(STATUS "Selected board V4L2") +elseif(BOARD STREQUAL "C610") + message(STATUS "Selected board C610") + set(SRCBOARD "GSTREAMER") +elseif(BOARD STREQUAL "GSTREAMER") + message(STATUS "Selected board GSTREAMER") elseif(BOARD STREQUAL "T31") message(STATUS "Selected board T31") elseif(BOARD STREQUAL "FH8626V100") @@ -47,9 +53,9 @@ set(INCS ${INCS_DIR}/com/amazonaws/kinesis/video/player/AudioPlayer.h ) set(SRCS - ${CMAKE_CURRENT_LIST_DIR}/source/${BOARD}/${BOARD}VideoCapturer.c - ${CMAKE_CURRENT_LIST_DIR}/source/${BOARD}/${BOARD}AudioCapturer.c - ${CMAKE_CURRENT_LIST_DIR}/source/${BOARD}/${BOARD}AudioPlayer.c + ${CMAKE_CURRENT_LIST_DIR}/source/${SRCBOARD}/${SRCBOARD}VideoCapturer.c + ${CMAKE_CURRENT_LIST_DIR}/source/${SRCBOARD}/${SRCBOARD}AudioCapturer.c + ${CMAKE_CURRENT_LIST_DIR}/source/${SRCBOARD}/${SRCBOARD}AudioPlayer.c ) if(NOT BOARD_DESTINATION_PLATFORM OR BOARD_DESTINATION_PLATFORM STREQUAL "") @@ -82,6 +88,16 @@ target_link_libraries(embedded-media-shared PRIVATE ${BOARD_LIBS_SHARED}) set_target_properties(embedded-media-shared PROPERTIES PUBLIC_HEADER "${INCS}") set_target_properties(embedded-media-shared PROPERTIES OUTPUT_NAME embedded-media) +if(BOARD STREQUAL "C610" OR BOARD STREQUAL "GSTREAMER") + find_package(PkgConfig REQUIRED) + pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0) + pkg_check_modules(GSTAPP REQUIRED gstreamer-app-1.0) + target_include_directories(embedded-media-static PRIVATE ${GSTREAMER_INCLUDE_DIRS} ${GSTAPP_INCLUDE_DIRS}) + target_link_libraries(embedded-media-static ${GSTREAMER_LIBRARIES} ${GSTAPP_LIBRARIES}) + target_include_directories(embedded-media-shared PRIVATE ${GSTREAMER_INCLUDE_DIRS} ${GSTAPP_INCLUDE_DIRS}) + target_link_libraries(embedded-media-shared ${GSTREAMER_LIBRARIES} ${GSTAPP_LIBRARIES}) +endif() + if(NOT FRAME_FILE_PATH_PREFIX OR FRAME_FILE_PATH_PREFIX STREQUAL "") set(FRAME_FILE_PATH_PREFIX ${CMAKE_CURRENT_LIST_DIR}/resources/frames/) message(STATUS "FRAME_FILE_PATH_PREFIX is ${FRAME_FILE_PATH_PREFIX}") diff --git a/README.md b/README.md old mode 100644 new mode 100755 index ba4e1c3..d872f90 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ [![file](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml) [![v4l2](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/v4l2.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/v4l2.yml) +[![Gstreamer](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/Gstreamer.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/Gstreamer.yml) +[![C610](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/C610/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/C610.yml) [![ingenic-t31](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/ingenic-t31.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/ingenic-t31.yml) [![fullhan-fh8626v100](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/fullhan-fh8626v100.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/fullhan-fh8626v100.yml) [![anyka-ak3918](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/anyka-ak3918.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/anyka-ak3918.yml) @@ -32,6 +34,8 @@ | FILE | Dummy boards that can capture from [sample frames](resources/frames/) | `-DBOARD=FILE` | [![file](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml) | | x86/x64 | Capture from V4L2 device or capture from [sample frames](resources/frames/) | `-DBOARD=V4L2`
or `-DBOARD=FILE` | [![v4l2](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/v4l2.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/v4l2.yml)[![file](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml) | | Raspberry Pi | Capture from V4L2 device or capture from [sample frames](resources/frames/) | `-DBOARD=V4L2`
or `-DBOARD=FILE` | [![v4l2](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/v4l2.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/v4l2.yml)[![file](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/file.yml) | +| Gstreamer | Capture from Gstreamer pipeline | `-DBOARD=GSTREAMER` | [![gstreamer](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/gstreamer.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/gstreamer.yml) | +| Qualcomm C610 | IPC SoC designed by Qualcomm | `-DBOARD=C610` | [![qualcomm-c610](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/qualcomm-c610.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/qualcomm-c610.yml) | | Ingenic T31 | IPC SoC designed by Ingenic | `-DBOARD=T31` | [![ingenic-t31](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/ingenic-t31.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/ingenic-t31.yml) | | Fullhan FH8626V100 | IPC SoC designed by Fullhan | `-DBOARD=FH8626V100` | [![fullhan-fh8626v100](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/fullhan-fh8626v100.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/fullhan-fh8626v100.yml) | | Anyka AK3918 | IPC SoC designed by Anyka | `-DBOARD=AK3918` | [![anyka-ak3918](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/anyka-ak3918.yml/badge.svg)](https://github.com/aws-samples/amazon-kinesis-video-streams-media-interface/actions/workflows/anyka-ak3918.yml) | diff --git a/samples/webrtc/CMakeLists.txt b/samples/webrtc/CMakeLists.txt index 357aede..e687de2 100755 --- a/samples/webrtc/CMakeLists.txt +++ b/samples/webrtc/CMakeLists.txt @@ -13,6 +13,7 @@ message(STATUS "EMBEDDED_MEDIA_INCLUDES_DIR - ${EMBEDDED_MEDIA_INCLUDES_DIR}") message(STATUS "EMBEDDED_MEDIA_LINK_DIR - ${EMBEDDED_MEDIA_LINK_DIR}") message(STATUS "USE_MUCLIBC - ${USE_MUCLIBC}") message(STATUS "BOARD_DESTINATION_PLATFORM - ${BOARD_DESTINATION_PLATFORM}") +message(STATUS "SRCBOARD - ${SRCBOARD}") set(WEBRTC_CMAKE_C_FLAGS "-fPIC -std=gnu99") diff --git a/source/GSTREAMER/GSTREAMERAudioCapturer.c b/source/GSTREAMER/GSTREAMERAudioCapturer.c new file mode 100755 index 0000000..d3a40a4 --- /dev/null +++ b/source/GSTREAMER/GSTREAMERAudioCapturer.c @@ -0,0 +1,256 @@ +/* + * Copyright 2023 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 + +#include "GSTREAMERCommon.h" +#include "GSTREAMERPort.h" +#include "com/amazonaws/kinesis/video/capturer/AudioCapturer.h" + +#define AUDIO_AAC_ADTS_HEADER_LENGTH 7 +#define AUDIO_AAC_ADTS_HEADER_CRC_LENGTH 2 + +#define GST_HANDLE_GET(x) GstAudioCapturer* audioHandle = (GstAudioCapturer*) ((x)) + +typedef struct { + AudioCapturerStatus status; + AudioCapability capability; + AudioFormat format; + AudioChannel channel; + AudioBitDepth bitDepth; + AudioSampleRate sampleRate; +} GstAudioCapturer; + +static GSTAppSinkSrc gstAppSinkAudioHandle; + +static int setStatus(AudioCapturerHandle handle, const AudioCapturerStatus newStatus) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + if (newStatus != audioHandle->status) { + audioHandle->status = newStatus; + LOG("AudioCapturer new status[%d]", newStatus); + } + + return 0; +} + +AudioCapturerHandle audioCapturerCreate(void) +{ + GstAudioCapturer* audioHandle = NULL; + + if (!(audioHandle = (GstAudioCapturer*) malloc(sizeof(GstAudioCapturer)))) { + LOG("OOM"); + return NULL; + } + + memset(audioHandle, 0, sizeof(GstAudioCapturer)); + + // Now we have sample frames for G.711 ALAW and AAC, MONO, 8k, 16 bits + audioHandle->capability.formats = (1 << (AUD_FMT_G711A - 1)) | (1 << (AUD_FMT_AAC - 1)); + audioHandle->capability.channels = (1 << (AUD_CHN_MONO - 1)); + audioHandle->capability.sampleRates = (1 << (AUD_SAM_8K - 1)); + audioHandle->capability.bitDepths = (1 << (AUD_BIT_16 - 1)); + + memset(&gstAppSinkAudioHandle, 0, sizeof(GSTAppSinkSrc)); + gstAppSinkAudioHandle.capturerHandle = (void*)audioHandle; + if (0 != initGstAudioSrc(&gstAppSinkAudioHandle)) { + LOG("initGstAudioSrc failed."); + return NULL; + } + + setStatus((AudioCapturerHandle) audioHandle, AUD_CAP_STATUS_STREAM_OFF); + + return (AudioCapturerHandle) audioHandle; +} + +AudioCapturerStatus audioCapturerGetStatus(const AudioCapturerHandle handle) +{ + if (!handle) { + return AUD_CAP_STATUS_NOT_READY; + } + + GST_HANDLE_GET(handle); + return audioHandle->status; +} + +int audioCapturerGetCapability(const AudioCapturerHandle handle, AudioCapability* pCapability) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + if (!pCapability) { + return -EINVAL; + } + + *pCapability = audioHandle->capability; + + return 0; +} + +int audioCapturerSetFormat(AudioCapturerHandle handle, const AudioFormat format, const AudioChannel channel, const AudioSampleRate sampleRate, + const AudioBitDepth bitDepth) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(audioHandle, AUD_CAP_STATUS_STREAM_OFF); + + switch (format) { + case AUD_FMT_G711A: + break; + + case AUD_FMT_AAC: + break; + + default: + LOG("Unsupported format %d", format); + return -EINVAL; + } + + switch (channel) { + case AUD_CHN_MONO: + break; + + default: + LOG("Unsupported channel num %d", channel); + return -EINVAL; + } + + switch (sampleRate) { + case AUD_SAM_8K: + break; + + default: + LOG("Unsupported sample rate %d", sampleRate); + return -EINVAL; + } + + switch (bitDepth) { + case AUD_BIT_16: + break; + + default: + LOG("Unsupported bit depth %d", bitDepth); + return -EINVAL; + } + + audioHandle->format = format; + audioHandle->channel = channel; + audioHandle->sampleRate = sampleRate; + audioHandle->bitDepth = bitDepth; + + return 0; +} + +int audioCapturerGetFormat(const AudioCapturerHandle handle, AudioFormat* pFormat, AudioChannel* pChannel, AudioSampleRate* pSampleRate, + AudioBitDepth* pBitDepth) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + *pFormat = audioHandle->format; + *pChannel = audioHandle->channel; + *pSampleRate = audioHandle->sampleRate; + *pBitDepth = audioHandle->bitDepth; + + return 0; +} + +int audioCapturerAcquireStream(AudioCapturerHandle handle) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + startGstAudioSrc(); + + return setStatus(handle, AUD_CAP_STATUS_STREAM_ON); +} + +int audioCapturerGetFrame(AudioCapturerHandle handle, void* pFrameDataBuffer, const size_t frameDataBufferSize, uint64_t* pTimestamp, + size_t* pFrameSize) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(audioHandle, AUD_CAP_STATUS_STREAM_ON); + + if (!pFrameDataBuffer || !pTimestamp || !pFrameSize) { + return -EINVAL; + } + + GstSample *sample; + GstElement *pipeline = NULL, *audioSink = NULL; + pipeline = getPipeline(AUDIO_CAPTURE_PIPELINE); + audioSink = gst_bin_get_by_name(GST_BIN(pipeline), GST_AUDIO_SINK_NAME); + g_signal_emit_by_name(audioSink, "pull-sample", &sample); + + if (sample) { + GstBuffer *buffer = gst_sample_get_buffer(sample); + if (buffer) { + GstMapInfo map; + if (gst_buffer_map(buffer, &map, GST_MAP_READ)) { + if (map.size < frameDataBufferSize) { + memcpy(pFrameDataBuffer, map.data, map.size); + *pFrameSize = map.size; + *pTimestamp = getEpochTimestampInUs(); + gst_buffer_unmap(buffer, &map); + gst_sample_unref(sample); + return 0; + } else { + LOG("(Audio)FrameDataBufferSize(%ld) < frameSize(%ld), frame dropped", frameDataBufferSize, map.size); + } + } + gst_buffer_unmap(buffer, &map); + } + gst_sample_unref(sample); + } + + return -EINVAL; +} + +int audioCapturerReleaseStream(AudioCapturerHandle handle) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(audioHandle, AUD_CAP_STATUS_STREAM_ON); + + stopGstAudioSrc(); + + return setStatus(handle, AUD_CAP_STATUS_STREAM_OFF); +} + +void audioCapturerDestroy(AudioCapturerHandle handle) +{ + if (!handle) { + return; + } + + GST_HANDLE_GET(handle); + + if (audioHandle->status == AUD_CAP_STATUS_STREAM_ON) { + audioCapturerReleaseStream(handle); + } + + deinitGstAudioSrc(); + + setStatus(handle, AUD_CAP_STATUS_NOT_READY); + + free(handle); +} diff --git a/source/GSTREAMER/GSTREAMERAudioPlayer.c b/source/GSTREAMER/GSTREAMERAudioPlayer.c new file mode 100755 index 0000000..638a3ad --- /dev/null +++ b/source/GSTREAMER/GSTREAMERAudioPlayer.c @@ -0,0 +1,213 @@ +/* + * Copyright 2023 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 + +#include "GSTREAMERCommon.h" +#include "GSTREAMERPort.h" +#include "com/amazonaws/kinesis/video/player/AudioPlayer.h" + +#define GST_HANDLE_GET(x) GstAudioPlayer* audioHandle = (GstAudioPlayer*) ((x)) + +typedef struct { + AudioPlayerStatus status; + AudioCapability capability; + AudioFormat format; + AudioChannel channel; + AudioBitDepth bitDepth; + AudioSampleRate sampleRate; +} GstAudioPlayer; + +static int setStatus(AudioPlayerHandle handle, const AudioPlayerStatus newStatus) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + if (newStatus != audioHandle->status) { + audioHandle->status = newStatus; + LOG("AudioPlayer set new status[%d]", newStatus); + } + + return 0; +} + +AudioPlayerHandle audioPlayerCreate(void) +{ + GstAudioPlayer* audioHandle = NULL; + + if (!(audioHandle = (GstAudioPlayer*) malloc(sizeof(GstAudioPlayer)))) { + LOG("OOM"); + return NULL; + } + + memset(audioHandle, 0, sizeof(GstAudioPlayer)); + + audioHandle->capability.formats = (1 << (AUD_FMT_G711A - 1)); + audioHandle->capability.channels = (1 << (AUD_CHN_MONO - 1)); + audioHandle->capability.sampleRates = (1 << (AUD_SAM_8K - 1)); + audioHandle->capability.bitDepths = (1 << (AUD_BIT_16 - 1)); + + if (0 != initGstAudioPlayer(NULL)) { + LOG("initGstAudioPlayer failed."); + return NULL; + } + + setStatus((AudioPlayerHandle) audioHandle, AUD_PLY_STATUS_STREAM_OFF); + + return (AudioPlayerHandle) audioHandle; +} + +AudioPlayerStatus audioPlayerGetStatus(const AudioPlayerHandle handle) +{ + if (!handle) { + return AUD_PLY_STATUS_NOT_READY; + } + + GST_HANDLE_GET(handle); + return audioHandle->status; +} + +int audioPlayerGetCapability(const AudioPlayerHandle handle, AudioCapability* pCapability) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + if (!pCapability) { + return -EINVAL; + } + + *pCapability = audioHandle->capability; + return 0; +} + +int audioPlayerSetFormat(AudioPlayerHandle handle, const AudioFormat format, const AudioChannel channel, const AudioSampleRate sampleRate, + const AudioBitDepth bitDepth) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(audioHandle, AUD_PLY_STATUS_STREAM_OFF); + + switch (format) { + case AUD_FMT_G711A: + break; + + default: + LOG("Unsupported format %d", format); + return -EINVAL; + } + + switch (channel) { + case AUD_CHN_MONO: + break; + + default: + LOG("Unsupported channel num %d", channel); + return -EINVAL; + } + + switch (sampleRate) { + case AUD_SAM_8K: + break; + + default: + LOG("Unsupported sample rate %d", sampleRate); + return -EINVAL; + } + + switch (bitDepth) { + case AUD_BIT_16: + break; + + default: + LOG("Unsupported bit depth %d", bitDepth); + return -EINVAL; + } + + audioHandle->format = format; + audioHandle->channel = channel; + audioHandle->sampleRate = sampleRate; + audioHandle->bitDepth = bitDepth; + + return 0; +} + +int audioPlayerGetFormat(const AudioPlayerHandle handle, AudioFormat* pFormat, AudioChannel* pChannel, AudioSampleRate* pSampleRate, + AudioBitDepth* pBitDepth) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + *pFormat = audioHandle->format; + *pChannel = audioHandle->channel; + *pSampleRate = audioHandle->sampleRate; + *pBitDepth = audioHandle->bitDepth; + + return 0; +} + +int audioPlayerAcquireStream(AudioPlayerHandle handle) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + startGstAudioPlayer(); + + return setStatus(handle, AUD_PLY_STATUS_STREAM_ON); +} + +int audioPlayerWriteFrame(AudioPlayerHandle handle, void* pData, const size_t size) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(audioHandle, AUD_PLY_STATUS_STREAM_ON); + + if (!pData || !size) { + return -EINVAL; + } + + int ret = writeToGstAudioPlayer(pData, size); + + return ret; +} + +int audioPlayerReleaseStream(AudioPlayerHandle handle) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(audioHandle, AUD_PLY_STATUS_STREAM_ON); + + stopGstAudioPlayer(); + + return setStatus(handle, AUD_PLY_STATUS_STREAM_OFF); +} + +void audioPlayerDestroy(AudioPlayerHandle handle) +{ + if (!handle) { + return; + } + GST_HANDLE_GET(handle); + + deinitGstAudioPlayer(); + + setStatus(handle, AUD_PLY_STATUS_NOT_READY); + + free(handle); +} diff --git a/source/GSTREAMER/GSTREAMERCommon.h b/source/GSTREAMER/GSTREAMERCommon.h new file mode 100755 index 0000000..c677ad3 --- /dev/null +++ b/source/GSTREAMER/GSTREAMERCommon.h @@ -0,0 +1,28 @@ +/* + * Copyright 2023 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 + +#define GST_HANDLE_NULL_CHECK(x) \ + if (!(x)) { \ + return -EINVAL; \ + } +#define GST_HANDLE_STATUS_CHECK(GstHandle, expectedStatus) \ + if ((GstHandle)->status != (expectedStatus)) { \ + return -EAGAIN; \ + } + +#define LOG(msg, ...) printf(msg "\n", ##__VA_ARGS__) diff --git a/source/GSTREAMER/GSTREAMERVideoCapturer.c b/source/GSTREAMER/GSTREAMERVideoCapturer.c new file mode 100755 index 0000000..9aaa111 --- /dev/null +++ b/source/GSTREAMER/GSTREAMERVideoCapturer.c @@ -0,0 +1,225 @@ +/* + * Copyright 2023 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 +#include +#include +#include + +#include "GSTREAMERCommon.h" +#include "GSTREAMERPort.h" +#include "com/amazonaws/kinesis/video/capturer/VideoCapturer.h" + +#define GST_HANDLE_GET(x) GstVideoCapturer* videoHandle = (GstVideoCapturer*) ((x)) + +typedef struct { + VideoCapturerStatus status; + VideoCapability capability; + VideoFormat format; + VideoResolution resolution; +} GstVideoCapturer; + +static GSTAppSinkSrc gstAppSinkVideHandle; + +static int setStatus(VideoCapturerHandle handle, const VideoCapturerStatus newStatus) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + if (newStatus != videoHandle->status) { + videoHandle->status = newStatus; + LOG("VideoCapturer new status[%d]", newStatus); + } + + return 0; +} + +VideoCapturerHandle videoCapturerCreate(void) +{ + GstVideoCapturer* videoHandle = NULL; + + if (!(videoHandle = (GstVideoCapturer*) malloc(sizeof(GstVideoCapturer)))) { + LOG("OOM"); + return NULL; + } + + memset(videoHandle, 0, sizeof(GstVideoCapturer)); + + // Now we have sample frames for H.264, 1080p + videoHandle->capability.formats = (1 << (VID_FMT_H264 - 1)); + videoHandle->capability.resolutions = (1 << (VID_RES_1080P - 1)); + + memset(&gstAppSinkVideHandle, 0, sizeof(GSTAppSinkSrc)); + gstAppSinkVideHandle.capturerHandle = (void*)videoHandle; + if (0 != initGstVideoSrc(&gstAppSinkVideHandle)) { + LOG("initGstVideoSrc failed."); + return NULL; + } + + setStatus((VideoCapturerHandle) videoHandle, VID_CAP_STATUS_STREAM_OFF); + + return (VideoCapturerHandle) videoHandle; +} + +VideoCapturerStatus videoCapturerGetStatus(const VideoCapturerHandle handle) +{ + if (!handle) { + return VID_CAP_STATUS_NOT_READY; + } + + GST_HANDLE_GET(handle); + return videoHandle->status; +} + +int videoCapturerGetCapability(const VideoCapturerHandle handle, VideoCapability* pCapability) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + if (!pCapability) { + return -EAGAIN; + } + + *pCapability = videoHandle->capability; + + return 0; +} + +int videoCapturerSetFormat(VideoCapturerHandle handle, const VideoFormat format, const VideoResolution resolution) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(videoHandle, VID_CAP_STATUS_STREAM_OFF); + + switch (format) { + case VID_FMT_H264: + break; + + default: + LOG("Unsupported format %d", format); + return -EINVAL; + } + + switch (resolution) { + case VID_RES_1080P: + break; + + default: + LOG("Unsupported resolution %d", resolution); + return -EINVAL; + } + + videoHandle->format = format; + videoHandle->resolution = resolution; + + return 0; +} + +int videoCapturerGetFormat(const VideoCapturerHandle handle, VideoFormat* pFormat, VideoResolution* pResolution) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + *pFormat = videoHandle->format; + *pResolution = videoHandle->resolution; + + return 0; +} + +int videoCapturerAcquireStream(VideoCapturerHandle handle) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + startGstVideoSrc(); + + return setStatus(handle, VID_CAP_STATUS_STREAM_ON); +} + +int videoCapturerGetFrame(VideoCapturerHandle handle, void* pFrameDataBuffer, const size_t frameDataBufferSize, uint64_t* pTimestamp, + size_t* pFrameSize) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(videoHandle, VID_CAP_STATUS_STREAM_ON); + + if (!pFrameDataBuffer || !pTimestamp || !pFrameSize) { + return -EINVAL; + } + + GstSample *sample; + GstElement *pipeline = NULL, *videoSink = NULL; + pipeline = getPipeline(VIDEO_CAPTURE_PIPELINE); + videoSink = gst_bin_get_by_name(GST_BIN(pipeline), GST_VIDEO_SINK_NAME); + g_signal_emit_by_name(videoSink, "pull-sample", &sample); + + if (sample) { + GstBuffer *buffer = gst_sample_get_buffer(sample); + if (buffer) { + GstMapInfo map; + if (gst_buffer_map(buffer, &map, GST_MAP_READ)) { + if (map.size < frameDataBufferSize) { + memcpy(pFrameDataBuffer, map.data, map.size); + *pFrameSize = map.size; + *pTimestamp = getEpochTimestampInUs(); + gst_buffer_unmap(buffer, &map); + gst_sample_unref(sample); + return 0; + } else { + LOG("(Video)FrameDataBufferSize(%ld) < frameSize(%ld), frame dropped", frameDataBufferSize, map.size); + } + } + gst_buffer_unmap(buffer, &map); + } + gst_sample_unref(sample); + } + + return -EINVAL; +} + +int videoCapturerReleaseStream(VideoCapturerHandle handle) +{ + GST_HANDLE_NULL_CHECK(handle); + GST_HANDLE_GET(handle); + + GST_HANDLE_STATUS_CHECK(videoHandle, VID_CAP_STATUS_STREAM_ON); + + stopGstVideoSrc(); + + return setStatus(handle, VID_CAP_STATUS_STREAM_OFF); +} + +void videoCapturerDestroy(VideoCapturerHandle handle) +{ + if (!handle) { + return; + } + + GST_HANDLE_GET(handle); + + if (videoHandle->status == VID_CAP_STATUS_STREAM_ON) { + videoCapturerReleaseStream(handle); + } + + deinitGstVideoSrc(); + + setStatus(handle, VID_CAP_STATUS_NOT_READY); + + free(handle); +}