From c096fbe41d0ebc836809ee5f05adfa11e55c9317 Mon Sep 17 00:00:00 2001 From: Stephen Barrett Date: Wed, 1 Apr 2020 17:10:03 +0100 Subject: [PATCH] Import of source (stable2) Source: https://gerrit.teamccp.com/rdk/components/generic/rdkc/thumbnail/generic SHA1: ab5682df27197228277a61f40e1f4a6f413733c0 Change-Id: I48d80f90a18188c1a38d4c8d31ed9b0cbd38d4d6 --- CONTRIBUTING.md | 11 + COPYING | 1 + LICENSE | 202 ++ Makefile | 34 + NOTICE | 2 + rdk_build.sh | 154 ++ rdk_env.xml | 27 + smart_thumbnail/Makefile | 71 + smart_thumbnail/include/smart_thumbnail.h | 256 +++ smart_thumbnail/main.cpp | 194 ++ smart_thumbnail/smart_thumbnail.cpp | 1756 +++++++++++++++++ smart_thumbnail_cvr_lite/Makefile | 70 + .../include/smart_thumbnail.h | 214 ++ smart_thumbnail_cvr_lite/main.cpp | 173 ++ smart_thumbnail_cvr_lite/smart_thumbnail.cpp | 1298 ++++++++++++ src/Makefile | 60 + src/thumbnailUpload.cpp | 1197 +++++++++++ src/thumbnailUpload.h | 170 ++ src/thumbnail_upload_main.cpp | 89 + 19 files changed, 5979 insertions(+) create mode 100644 CONTRIBUTING.md create mode 120000 COPYING create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 NOTICE create mode 100755 rdk_build.sh create mode 100644 rdk_env.xml create mode 100644 smart_thumbnail/Makefile create mode 100644 smart_thumbnail/include/smart_thumbnail.h create mode 100644 smart_thumbnail/main.cpp create mode 100644 smart_thumbnail/smart_thumbnail.cpp create mode 100644 smart_thumbnail_cvr_lite/Makefile create mode 100644 smart_thumbnail_cvr_lite/include/smart_thumbnail.h create mode 100644 smart_thumbnail_cvr_lite/main.cpp create mode 100644 smart_thumbnail_cvr_lite/smart_thumbnail.cpp create mode 100755 src/Makefile create mode 100644 src/thumbnailUpload.cpp create mode 100644 src/thumbnailUpload.h create mode 100644 src/thumbnail_upload_main.cpp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e2985fa --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,11 @@ +Contributing +============ + +If you wish to make code contributions to this project, the master source is hosted at [code.rdkcentral.com](https://code.rdkcentral.com/r/#/admin/projects/rdkc/components/opensource/thumbnail. +You can submit your changes for review via that site. + +Please follow the [workflow](https://wiki.rdkcentral.com/display/CMF/Gerrit+Development+Workflow) when making a contribution. + +In order to contribute code, first-time users are requested to agree to the [license](https://wiki.rdkcentral.com/signup.action). + +There is a GitHub [mirror](https://github.com/rdkcmf/rdkc-thumbnail) of this project. Pull requests to the mirror will be ignored. diff --git a/COPYING b/COPYING new file mode 120000 index 0000000..7a694c9 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +LICENSE \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License 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. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4683042 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +modules += src +modules += smart_thumbnail +modules += smart_thumbnail_cvr_lite + +all: + @for m in $(modules); do echo $$m; make -C $$m $@ || exit 1; done + +clean: + @for m in $(modules); do echo $$m; make -C $$m $@ || exit 1; done + +install: + @for m in $(modules); do echo $$m; make -C $$m $@ || exit 1; done + +.PHONY: all clean install + diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..e152845 --- /dev/null +++ b/NOTICE @@ -0,0 +1,2 @@ +Copyright 2018 RDK Management +Licensed under the Apache License, Version 2.0 diff --git a/rdk_build.sh b/rdk_build.sh new file mode 100755 index 0000000..a6b77ff --- /dev/null +++ b/rdk_build.sh @@ -0,0 +1,154 @@ +#!/bin/bash +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +####################################### +# +# Build Framework standard script for +# +# webrtc source code + +# use -e to fail on any shell issue +# -e is the requirement from Build Framework +set -e + +# default PATHs - use `man readlink` for more info +# the path to combined build +export RDK_PROJECT_ROOT_PATH=${RDK_PROJECT_ROOT_PATH-`readlink -m ..`} +export COMBINED_ROOT=$RDK_PROJECT_ROOT_PATH + +# path to build script (this script) +export RDK_SCRIPTS_PATH=${RDK_SCRIPTS_PATH-`readlink -m $0 | xargs dirname`} + +# path to components sources and target +export RDK_SOURCE_PATH=${RDK_SOURCE_PATH-`readlink -m .`} +export RDK_TARGET_PATH=${RDK_TARGET_PATH-$RDK_SOURCE_PATH} + +#default component name +export RDK_COMPONENT_NAME=${RDK_COMPONENT_NAME-`basename $RDK_SOURCE_PATH`} +export BUILDS_DIR=$RDK_PROJECT_ROOT_PATH +export RDK_DIR=$BUILDS_DIR +export ENABLE_RDKC_LOGGER_SUPPORT=true +export DCA_PATH=$RDK_SOURCE_PATH + +if [ "$XCAM_MODEL" == "SCHC2" ]; then +. ${RDK_PROJECT_ROOT_PATH}/build/components/amba/sdk/setenv2 +elif [ "$XCAM_MODEL" == "SERXW3" ] || [ "$XCAM_MODEL" == "SERICAM2" ]; then +. ${RDK_PROJECT_ROOT_PATH}/build/components/sdk/setenv2 +else #No Matching platform + echo "Source environment that include packages for your platform. The environment variables PROJ_PRERULE_MAK_FILE should refer to the platform s PreRule make" +fi + +export PLATFORM_SDK=${RDK_TOOLCHAIN_PATH} +export FSROOT=$RDK_FSROOT_PATH + +# parse arguments +INITIAL_ARGS=$@ + +function usage() +{ + set +x + echo "Usage: `basename $0` [-h|--help] [-v|--verbose] [action]" + echo " -h --help : this help" + echo " -v --verbose : verbose output" + echo + echo "Supported actions:" + echo " configure, clean, build (DEFAULT), rebuild, install" +} + +ARGS=$@ + + +# This Function to perform pre-build configurations before building plugin code +function configure() +{ + echo "Pre-build configurations for code ..." + + cd $RDK_SOURCE_PATH + clean + +} + +# This Function to perform clean the build if any exists already +function clean() +{ + echo "Start Clean" + cd $RDK_SOURCE_PATH + + make clean + +} + +# This Function peforms the build to generate the webrtc.node +function build() +{ + configure + echo "Configure is done" + + cd $RDK_SOURCE_PATH + + echo "RDK_SOURCE_PATH :::::: $RDK_SOURCE_PATH" + make all + + echo "thumbnail build is done" + echo "Starting make install... $RDK_COMPONENT_NAME" + install +} + +# This Function peforms the rebuild to generate the webrtc.node +function rebuild() +{ + clean + build +} + +# This functions performs installation of webrtc-streaming-node output created into sercomm firmware binary +function install() +{ + echo "Start thumbnail Installation" + + cd $RDK_SOURCE_PATH + + make install + + echo "thumbnail Installation is done" +} + +# run the logic +#these args are what left untouched after parse_args +HIT=false + +for i in "$ARGS"; do + case $i in + configure) HIT=true; configure ;; + clean) HIT=true; clean ;; + build) HIT=true; build ;; + rebuild) HIT=true; rebuild ;; + install) HIT=true; install ;; + *) + #skip unknown + ;; + esac +done + +# if not HIT do build by default +if ! $HIT; then + build +fi + diff --git a/rdk_env.xml b/rdk_env.xml new file mode 100644 index 0000000..7e411bf --- /dev/null +++ b/rdk_env.xml @@ -0,0 +1,27 @@ + + + + + sdk,rdklogger,video-analytics,utility,sysapps,sysint + + + diff --git a/smart_thumbnail/Makefile b/smart_thumbnail/Makefile new file mode 100644 index 0000000..76549b5 --- /dev/null +++ b/smart_thumbnail/Makefile @@ -0,0 +1,71 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +# Add dependent libraries +USE_OPENCV = yes +USE_HTTPCLIENT = yes +USE_RFCCONFIG = yes +USE_CONFIGMGR = yes +USE_RTMESSAGE = yes +USE_PLUGINS = yes + +include ${RDK_PROJECT_ROOT_PATH}/utility/AppsRule.mak +LIBS = $(LIBFLAGS) + +USE_LEGACY_CONFIG_MGR = no + +ifeq ($(XCAM_MODEL), SCHC2) +CFLAGS += -DXCAM2 +endif + +CFLAGS += -I./include +CFLAGS += -std=c++14 -fPIC -Wall -Wextra + +CFLAGS += -DUSE_FILE_UPLOAD + +ifeq ($(USE_LEGACY_CONFIG_MGR), yes) +CFLAGS += -DLEGACY_CFG_MGR +endif + +CFLAGS += -DUSE_MFRLIB + +SRC += smart_thumbnail.cpp main.cpp +OBJ = $(SRC:.cpp=.o) + +TARGET = smartthumbnail + +all: $(TARGET) + +RM = rm +INSTALL = install + +$(TARGET): $(OBJ) + $(CXX) -o $(@) $^ $(LIBS) + +%.o:%.cpp + $(CXX) -c $< $(CFLAGS) -o $@ + +install: + $(INSTALL) -D $(TARGET) ${RDK_SDROOT}/usr/local/bin/$(TARGET) + +uninstall: + $(RM) -f $(RDK_SDROOT)/usr/local/bin/$(TARGET) + +clean: + $(RM) -rf $(TARGET) $(OBJ) diff --git a/smart_thumbnail/include/smart_thumbnail.h b/smart_thumbnail/include/smart_thumbnail.h new file mode 100644 index 0000000..bbf691f --- /dev/null +++ b/smart_thumbnail/include/smart_thumbnail.h @@ -0,0 +1,256 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +*/ +#ifndef __SMART_THUMBNAIL_H__ +#define __SMART_THUMBNAIL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef LEGACY_CFG_MGR +#include "dev_config.h" +#else +#include "polling_config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef USE_MFRLIB +#include "mfrApi.h" +#endif +#ifdef __cplusplus +} +#endif + +#include "rdk_debug.h" +#include "rtConnection.h" +#include "rtLog.h" +#include "rtMessage.h" +#include "HttpClient.h" +#include "RdkCVideoCapturer.h" +#include "RdkCPluginFactory.h" +#include "RFCCommon.h" +#include "opencv2/opencv.hpp" + +#define CACHE_SMARTTHUMBNAIL "/tmp/cache_smart_thumbnail.txt" + +#define FW_NAME_MAX_LENGTH 512 +#define CONFIG_STRING_MAX (256) +#define YUV_HRES_BUFFER_ID 0 +#define YUV_HRES_FRAME_WIDTH 1280 +#define YUV_HRES_FRAME_HEIGHT 720 + +#ifndef XCAM2 +#define STN_HRES_BUFFER_ID 0 +#else +#define STN_HRES_BUFFER_ID 2 +#endif + +//actual width and height of smart thumbnail to be uploaded +#define STN_FRAME_WIDTH 212 +#define STN_FRAME_HEIGHT 119 + +#define STN_DEFAULT_DNS_CACHE_TIMEOUT 60 + +//default width and height of smart thumbnail +#define STN_DEFAULT_WIDTH 640 +#define STN_DEFAULT_HEIGHT 480 + +#define STN_TIMESTAMP_TAG "timestamp" +#define STN_UPLOAD_TIME_INTERVAL 30 + +#define STN_PATH "/tmp" +//#define STN_PATH "." +#define STN_UPLOAD_SEND_LEN 2048 +#define STN_COMPRESSION_SCALE 40 +#define STN_DATA_MAX_SIZE 1024*1024 //1 MB +#define STN_TSTAMP_SIZE 50 +#define STN_DEFAULT_EVT_QUIET_TIME 30 +#define STN_MAX_PAYLOAD_COUNT 4 + +#define STN_TRUE "true" +#define STN_FALSE "false" + +#define RTMSG_DYNAMIC_LOG_REQ_RES_TOPIC "RDKC.ENABLE_DYNAMIC_LOG" + +#define STN_MAX( a, b ) ( ( a > b) ? a : b ) +#define STN_MIN( a, b ) ( ( a < b) ? a : b ) + +typedef enum { + STH_ERROR = -1, + STH_SUCCESS, + STH_NO_PAYLOAD +}STH_STATUS; + +typedef enum { + CVR_CLIP_GEN_START = 0, + CVR_CLIP_GEN_END, + CVR_CLIP_GEN_UNKNOWN +}CVR_CLIP_GEN_STATUS; + +typedef enum { + CVR_UPLOAD_OK = 0, + CVR_UPLOAD_FAIL, + CVR_UPLOAD_CONNECT_ERR, + CVR_UPLOAD_SEND_ERR, + CVR_UPLOAD_RESPONSE_ERROR, + CVR_UPLOAD_TIMEOUT, + CVR_UPLOAD_MAX, + CVR_UPLOAD_CURL_ERR +}CVR_UPLOAD_STATUS; + +typedef struct { + char fname[64]; + uint64_t tstamp; +}STHPayload; + +typedef struct { + uint32_t boundingBoxXOrd; + uint32_t boundingBoxYOrd; + uint32_t boundingBoxWidth; + uint32_t boundingBoxHeight; + uint64_t currTime; + cv::Mat maxBboxObjYUVFrame; +}objFrameData; + +class SmartThumbnail +{ + public: + static SmartThumbnail* getInstance(); + //Initialize the buffers and starts msg monitoring, upload thread. + STH_STATUS init(); + //get upload status + bool getUploadStatus(); + //set upload status + STH_STATUS setUploadStatus(bool status); + //Upload smart thumbnail data + void uploadPayload(); + //notify start or end of smart thumbnail process + STH_STATUS notify(const char* status); + //call Smart thumbnail destructor and deallocates dynamic allocated memory. + STH_STATUS destroy(); + //Thread routine to receive data + STH_STATUS receiveRtmessage(); + + static void sigHandler(int signum); + + private: + SmartThumbnail(); + ~SmartThumbnail(); + STH_STATUS saveSTN(); + STH_STATUS addSTN(); + STH_STATUS delSTN(char* uploadFname); + void printSTNList(); + STH_STATUS createPayload(char* uploadFname); + STH_STATUS getTnUploadConf(); + STH_STATUS getEventConf(); + bool checkEnabledRFCFeature(char* rfcFeatureFname, char* featureName); + //sets the camera firmware version. + int setCameraImageName(char* out); + //sets the camera firmware version. + int setModelName(char* modelName); + //sets the camera's mac address. + int setMacAddress(char* macAddress); + // registers Callback for rtmessage + STH_STATUS rtMsgInit(); + //reset smart thumbnail resources + void resetResources(); + //Resize the cropped area keeping the aspect ratio. + STH_STATUS resizeAspect(cv::Mat im, int w, int h, cv::Mat& im_resized); + cv::Point2f getActualCentroid(cv::Rect boundRect); + cv::Point2f alignCentroid(cv::Point2f orgCenter, cv::Mat origFrame, cv::Size cropSize); + cv::Size getCropSize(cv::Rect boundRect,double w,double h); + cv::Rect getRelativeBoundingBox(cv::Rect boundRect, cv::Size cropSize, cv::Point2f allignedCenter); + void stringifyEventDateTime(char* strEvtDateTime , size_t evtdatetimeSize, time_t evtDateTime); + //Updates object frame + void updateObjFrameData(int32_t boundingBoxXOrd,int32_t boundingBoxYOrd,int32_t boundingBoxWidth,int32_t boundingBoxHeight,uint64_t currTime); + int retryAtExpRate(); + + //Read the High resolution YUV frame. + static void onMsgCaptureFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + //Generate the RGB object detection frame. + static void onMsgProcessFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + static void onMsgCvr(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + static void onMsgCvrUpload(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + static void onMsgRefreshTnUpload(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + static void dynLogOnMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + //Routine to upload STN Payload + static void uploadSTN(); + + static volatile bool termFlag; + static volatile bool tnUploadConfRefreshed; + + static SmartThumbnail* smartThInst; + RdkCPluginFactory* pluginFactory; + RdkCVideoCapturer* recorder; + RDKC_PLUGIN_YUVInfo* hres_frame_info; + //static bool hres_yuvDataMemoryAllocationDone; + + bool logMotionEvent; + + bool uploadReady; + //static bool rtmessageSTHThreadExit; + //static bool uploadSTHThreadExit; + bool isPayloadAvailable; + std::mutex stnMutex; + std::mutex uploadMutex; + std::condition_variable cv; + rtConnection connectionRecv; + rtConnection connectionSend; + rtError err; + int maxBboxArea; + objFrameData ofData; + //unsigned char* hres_yuvData; + int hres_y_size; + int hres_uv_size; + int hres_y_height; + int hres_y_width; + bool isHresFrameReady; + std::vector STNList; + bool cvrClipGenStarted; + + HttpClient* httpClient; + int dnsCacheTimeout; + STHPayload currSTN; + STHPayload payload; + + uint64_t motion_time; + int32_t event_quiet_time; + + char smtTnUploadURL[CONFIG_STRING_MAX]; + char smtTnAuthCode[AUTH_TOKEN_MAX]; + + int sTnHeight; + int sTnWidth; + char uploadFname[CONFIG_STRING_MAX]; + char currSTNFname[CONFIG_STRING_MAX]; + static int waitingInterval; + cv::Rect relativeBBox; +}; + +#endif //__SMART_THUMBNAIL_H__ diff --git a/smart_thumbnail/main.cpp b/smart_thumbnail/main.cpp new file mode 100644 index 0000000..4346d6a --- /dev/null +++ b/smart_thumbnail/main.cpp @@ -0,0 +1,194 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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 "smart_thumbnail.h" +#include + +#if 0 +/** @description: Checks if the feature is enabled via RFC + * @param[in] rfc_feature_fname: RFC feature filename + * @param[in] feature_name: RFC parameter name + * @return: bool + */ +bool checkEnabledRFCFeature(char* rfcFeatureFname, char* featureName) +{ + /* set cvr audio through RFC files */ + char value[10] = {0}; + + if((NULL == rfcFeatureFname) || + (NULL == featureName)) { + return STN_FALSE; + } + + /* Check if RFC configuration file exists */ + if(0 == IsRFCFileAvailable(rfcFeatureFname)) { + /* Get the value from RFC file */ + if( STH_SUCCESS == GetValueFromRFCFile(rfcFeatureFname, featureName, value) ) { + if( strcmp(value, STN_TRUE) == 0) { + //RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): %s is enabled via RFC.\n",__FILE__, __LINE__, featureName); + return true; + } else { + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDKSMARTTHUMBNAIL","%s(%d): %s is disabled via RFC.\n",__FILE__, __LINE__, featureName); + return false; + } + } + /* If RFC file is not present, disable the feature */ + } else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): rfc feature file %s is not present.\n",__FILE__, __LINE__, rfcFeatureFname); + return false; + } +} +#endif + +int main(int argc, char** argv) +{ + SmartThumbnail *smTnInstance = NULL; + STH_STATUS status = STH_SUCCESS; + time_t remainingTime = 0; + + struct timespec currTime; + struct timespec startTime; + memset(&startTime, 0, sizeof(startTime)); + memset(&currTime, 0, sizeof(currTime)); + + signal(SIGTERM, SmartThumbnail::sigHandler); + signal(SIGINT, SmartThumbnail::sigHandler); + + //initialize rdklogger + rdk_logger_init("/etc/debug.ini"); + //initialize RFC + RFCConfigInit(); + + //create instance + smTnInstance = SmartThumbnail::getInstance(); + if (!smTnInstance) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating Smart thumbnail instance.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + //initialize smart thumbnail + status = smTnInstance-> init(); + if (STH_ERROR == status) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating Smart thumbnail instance.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Notify xvision and cvr daemon.\n", __FILE__, __LINE__); +#if 0 + //Initially sleep for upload interval time, to allow smart thumbnail to be generated. + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Putting the Smart Thumnail Upload to sleep for %d seconds.\n", __FILE__, __LINE__, STN_UPLOAD_TIME_INTERVAL); + sleep(STN_UPLOAD_TIME_INTERVAL); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart Thumbnail Upload is ready.\n", __FILE__, __LINE__); +#endif + //Notify start status + smTnInstance-> notify("start"); + + smTnInstance-> receiveRtmessage(); +#if 0 + while (true) { + +#if 0 + //clock the start time + memset(&startTime, 0, sizeof(startTime)); + clock_gettime(CLOCK_REALTIME, &startTime); +#endif + + //exit app if smt Thumbnail is disabled via RFC + if(!checkEnabledRFCFeature(RFC_SMART_TN_UPLOAD, SMART_TN_UPLOAD)) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exiting smart thumbnail, Disable via RFC!!!\n", __FILE__, __LINE__); + break; + } + +#if 0 + //create payload + status = smTnInstance->createPayload(); + if ( (STH_NO_PAYLOAD == status) || + (STH_ERROR == status) ) { + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Unable to create payload for smart thumnail, hence skipping!!!\n", __FILE__, __LINE__); + + //clock current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Time spent for payload creation %d seconds!!!\n", __FILE__, __LINE__, (currTime.tv_sec - startTime.tv_sec)); + + // sleep maximum of smart thumbnail time interval(~15 seconds) + if( (currTime.tv_sec - startTime.tv_sec) >= STN_UPLOAD_TIME_INTERVAL ) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exceed upload time interval!!!\n", __FILE__, __LINE__); + continue; + } else { + remainingTime = STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Sleep for remaining %d seconds!!!\n", __FILE__, __LINE__, remainingTime); + sleep (remainingTime); + continue; + } + } + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Created payload for smart thumnail successfully.. Going to upload now!!!\n", __FILE__, __LINE__); + + // clock the current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): currTime.tv_sec %d startTime.tv_sec %d (currTime.tv_sec - startTime.tv_sec)!!!\n", __FILE__, __LINE__, currTime.tv_sec, startTime.tv_sec, (currTime.tv_sec - startTime.tv_sec)); + + smTnInstance->uploadPayload(STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec)); + + // clock the current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Time spent to upload smart thumbnail %d seconds!!!\n", __FILE__, __LINE__, (currTime.tv_sec - startTime.tv_sec)); + if( (currTime.tv_sec - startTime.tv_sec) >= STN_UPLOAD_TIME_INTERVAL ) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exceed upload time interval!!!\n", __FILE__, __LINE__); + continue; + } + else { + //calculate the time needed to sleep for next interval. + remainingTime = STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Sleep for remaining %d seconds!!!\n", __FILE__, __LINE__, remainingTime); + sleep (remainingTime); + } + } +#endif + +#if 1 + if(smTnInstance -> getUploadStatus()) { + smTnInstance->uploadPayload(STN_UPLOAD_TIME_INTERVAL); + //smTnInstance->setUploadStatus(false); + } +#endif + + + } +#endif + + + //notify exit status + smTnInstance -> notify("stop"); + + //destroy smartThumbnail instance + if(smTnInstance) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Deleting smart thumnail instance!!!\n", __FILE__, __LINE__); + smTnInstance-> destroy(); + } + RFCRelease(); + return 0; +} + diff --git a/smart_thumbnail/smart_thumbnail.cpp b/smart_thumbnail/smart_thumbnail.cpp new file mode 100644 index 0000000..d42d405 --- /dev/null +++ b/smart_thumbnail/smart_thumbnail.cpp @@ -0,0 +1,1756 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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 "smart_thumbnail.h" + +SmartThumbnail* SmartThumbnail::smartThInst = NULL; +volatile bool SmartThumbnail::termFlag = false; +volatile bool SmartThumbnail::tnUploadConfRefreshed = false; +int SmartThumbnail::waitingInterval = 1 ; + +/** @description: Constructor + * @param[in] void + * @return: void + */ +//SmartThumbnail::SmartThumbnail():hres_yuvData(NULL), +SmartThumbnail::SmartThumbnail():recorder(NULL), + httpClient(NULL), + hres_y_size(0), + hres_uv_size(0), + hres_y_height(0), + hres_y_width(0), + maxBboxArea(0), + isHresFrameReady(false), + event_quiet_time(STN_DEFAULT_EVT_QUIET_TIME), + uploadReady(false), + isPayloadAvailable(false), + cvrClipGenStarted(false), + dnsCacheTimeout(STN_DEFAULT_DNS_CACHE_TIMEOUT), + sTnHeight(STN_DEFAULT_HEIGHT), + sTnWidth(STN_DEFAULT_WIDTH) +{ + memset(uploadFname, 0, sizeof(uploadFname)); + memset(smtTnUploadURL, 0, sizeof(smtTnUploadURL)); + memset(smtTnAuthCode, 0, sizeof(smtTnAuthCode)); + memset(currSTNFname, 0, sizeof(currSTNFname)); + + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart thumbnail constructor invoked.\n", __FUNCTION__, __LINE__); +} + +/** @description: Destructor + * @param[in] void + * @return: void + */ +SmartThumbnail::~SmartThumbnail() +{ + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart thumbnail destructor invoked.\n", __FUNCTION__, __LINE__); +} + +void SmartThumbnail::sigHandler(int signum){ + if(SIGTERM == signum){ + //std::cout << "received SIGTERM\n"; + termFlag = true; + } else if( SIGINT == signum){ + //std::cout << "received SIGINT\n"; + termFlag = true; + } +} + +/** @description: creates the instance for smart thumbnail + * @param[in] void + * @return: pointer to instance of SmartThumbnail + */ +SmartThumbnail *SmartThumbnail::getInstance() +{ + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail instance.\n", __FUNCTION__, __LINE__); + if (!smartThInst) { + smartThInst = new SmartThumbnail(); + } + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart thumbnail instance created.\n", __FUNCTION__, __LINE__); + + return smartThInst; +} + +/** @description: initialize smart thumbnail + * @param[in] void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::init() +{ + +#ifdef LEGACY_CFG_MGR + char usrVal[CONFIG_STRING_MAX]; + char *configParam = NULL; + + //initializing config Manager + if (STH_SUCCESS != config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error loading config manager.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + // get upload url + memset(usrVal, 0, sizeof(usrVal)); + if (STH_SUCCESS != rdkc_get_user_setting(SMART_THUMBNAIL_UPLOAD_URL,usrVal)) { + configParam=(char*)rdkc_envGet(SMART_THUMBNAIL_UPLOAD_URL); + } else { + configParam =usrVal; + } + + if (NULL != configParam) { + strcpy(smtTnUploadURL,configParam); + configParam = NULL; + } + + // get auth code + memset(usrVal, 0, sizeof(usrVal)); + if (STH_SUCCESS != rdkc_get_user_setting(SMART_THUMBNAIL_AUTH_CODE ,usrVal)) { + configParam=(char*)rdkc_envGet(SMART_THUMBNAIL_AUTH_CODE ); + } else { + configParam =usrVal; + } + if (NULL != configParam) { + strcpy(smtTnAuthCode,configParam); + configParam = NULL; + } +#else + if(STH_SUCCESS == getTnUploadConf()) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): getTnUploadConf success!!", __FUNCTION__, __LINE__); + } + + if(STH_SUCCESS == getEventConf()) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): getEventConf success!!", __FUNCTION__, __LINE__); + } + +#endif + + // update mac/model/camera image +#if 0 + memset(modelName, 0, sizeof(modelName)); + memset(macAddress, 0, sizeof(macAddress)); + memset(firmwareName, 0,sizeof(firmwareName)); + setMacAddress(); + setModelName(); + setCameraImageName(firmwareName); +#endif + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail upload URL: %s \n",__FUNCTION__, __LINE__,smtTnUploadURL); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail auth code: %s \n",__FUNCTION__, __LINE__,smtTnAuthCode); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail event quiet time: %d \n",__FUNCTION__, __LINE__,event_quiet_time); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail height: %d \n",__FUNCTION__, __LINE__,sTnHeight); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail width: %d \n",__FUNCTION__, __LINE__,sTnWidth); + + //creating plugin factory instance. + pluginFactory = CreatePluginFactoryInstance(); + if (!pluginFactory) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating plugin factory instance.\n", __FUNCTION__, __LINE__); + return STH_ERROR; + } + + //create recorder + recorder = ( RdkCVideoCapturer* )pluginFactory->CreateVideoCapturer(); + if (!recorder) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating instance of plugin recorder.\n", __FUNCTION__, __LINE__); + return STH_ERROR; + } + + //allocate memory for yuv high resolution buffer + hres_frame_info = (RDKC_PLUGIN_YUVInfo *) malloc(sizeof(RDKC_PLUGIN_YUVInfo)); + if (NULL == hres_frame_info) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Frame malloc error for high res frame info. \n", __FUNCTION__ , __LINE__); + return STH_ERROR; + } + + //initialize http client + httpClient = new HttpClient(); + + /* Open the URL */ + if (NULL != httpClient) { + //httpClient->open(smtTnUploadURL, dnsCacheTimeout, STN_MAX_UPLOAD_TIMEOUT); + httpClient->open(smtTnUploadURL, dnsCacheTimeout); + } else { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to open the URL\n", __FUNCTION__, __LINE__); + } + + rtMsgInit(); + + //Initialize upload routine + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail upload thread.\n", __FUNCTION__, __LINE__); + std::thread uploadPayloadThread(uploadSTN); + uploadPayloadThread.detach(); + + return STH_SUCCESS; +} + +/** @description: Checks if the feature is enabled via RFC + * @param[in] rfc_feature_fname: RFC feature filename + * @param[in] feature_name: RFC parameter name + * @return: bool + */ +bool SmartThumbnail::checkEnabledRFCFeature(char* rfcFeatureFname, char* featureName) +{ + /* set cvr audio through RFC files */ + char value[10] = {0}; + + if((NULL == rfcFeatureFname) || + (NULL == featureName)) { + return STN_FALSE; + } + + /* Check if RFC configuration file exists */ + if(0 == IsRFCFileAvailable(rfcFeatureFname)) { + /* Get the value from RFC file */ + if( STH_SUCCESS == GetValueFromRFCFile(rfcFeatureFname, featureName, value) ) { + if( strcmp(value, STN_TRUE) == 0) { + //RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): %s is enabled via RFC.\n",__FILE__, __LINE__, featureName); + return true; + } else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDKSMARTTHUMBNAIL","%s(%d): %s is disabled via RFC.\n",__FILE__, __LINE__, featureName); + return false; + } + } + /* If RFC file is not present, disable the feature */ + } else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): rfc feature file %s is not present.\n",__FILE__, __LINE__, rfcFeatureFname); + return false; + } +} + +/** @description: Get thumbnail upload conf + * @param[in] void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::getTnUploadConf() +{ + tn_provision_info_t *stnCfg = NULL; + bool retry = true; + + // Read Thumbnail config. + stnCfg = (tn_provision_info_t*) malloc(sizeof(tn_provision_info_t)); + + if (NULL == stnCfg) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error allocating memory.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + if (STH_SUCCESS != polling_config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error initializing polling config.\n", __FILE__, __LINE__); + if (stnCfg) { + free(stnCfg); + } + return STH_ERROR; + } + + while (retry) { + if (STH_SUCCESS != readTNConfig(stnCfg)) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error reading TNConfig.\n", __FILE__, __LINE__); + } else { + break; + } + //Sleep 10 sec before retrying + sleep(10); + } + + // get url and auth + strcpy(smtTnUploadURL, stnCfg -> url); + strcpy(smtTnAuthCode, stnCfg -> auth_token); + sTnHeight = atoi(stnCfg -> height); + sTnWidth = atoi(stnCfg -> width); + + if (stnCfg) { + free(stnCfg); + stnCfg = NULL; + } + + polling_config_exit(); + + return STH_SUCCESS; +} + +/** @description: Get Event conf + * @param[in] void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::getEventConf() +{ + bool retry = true; + events_provision_info_t *eventsCfg = NULL; + + // Read event config. + eventsCfg = (events_provision_info_t*) malloc(sizeof(events_provision_info_t)); + + if (NULL == eventsCfg) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error allocating memory.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + if (STH_SUCCESS != polling_config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error initializing polling config.\n", __FILE__, __LINE__); + if (eventsCfg) { + free(eventsCfg); + } + return STH_ERROR; + } + + while (retry) { + if (STH_SUCCESS != readEventConfig(eventsCfg)) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error reading EVENTS Config.\n", __FILE__, __LINE__); + } else { + break; + } + //Sleep 10 sec before retrying + sleep(10); + } + + // get event quiet interval + if (strlen(eventsCfg->quite_interval) > 0) { + event_quiet_time = atoi(eventsCfg->quite_interval); + } + + if (eventsCfg) { + free(eventsCfg); + eventsCfg = NULL; + } + + polling_config_exit(); + + return STH_SUCCESS; +} + +/** @description: saves smart thumbnail to file + * @param[in] : void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::saveSTN() +{ + //std::pair objPair; + + STH_STATUS ret = STH_ERROR; + + cv::Rect unionBox; + cv::Mat lHresRGBMat; + cv::Mat croppedObj; + + char fPath [64] = {0}; + //cv::Mat resizedCroppedObject; + cv::Mat resizedRGBMat; +#ifdef USE_FILE_UPLOAD + struct tm* tv = NULL; + struct timespec currTime; +#endif + //{ + //Acquire lock + //std::unique_lock lock(stnMutex); + if (isPayloadAvailable) { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Payload is available.\n", __FILE__, __LINE__); + currSTN.tstamp = ofData.currTime; + + // matrix to store color image + lHresRGBMat = cv::Mat(hres_y_height, hres_y_width, CV_8UC4); + // convert the frame to BGR format + cv::cvtColor(ofData.maxBboxObjYUVFrame,lHresRGBMat, cv::COLOR_YUV2BGR_NV12); + + unionBox.x = ofData.boundingBoxXOrd; + unionBox.y = ofData.boundingBoxYOrd; + unionBox.width = ofData.boundingBoxWidth; + unionBox.height = ofData.boundingBoxHeight; + + // extracted the below logic from server scala code + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):unionBox.x %d unionBox.y %d unionBox.height %d unionBox.width %d\n", __FILE__, __LINE__, unionBox.x, unionBox.y, unionBox.height, unionBox.width); + cv::Point2f orgCenter = getActualCentroid(unionBox); + cv::Size cropSize = getCropSize(unionBox, sTnWidth, sTnHeight); + cv::Point2f allignedCenter = alignCentroid(orgCenter, lHresRGBMat, cropSize); + getRectSubPix(lHresRGBMat, cropSize, allignedCenter, croppedObj); + relativeBBox = getRelativeBoundingBox(unionBox, cropSize, allignedCenter); + +#if 0 + // create cropped object + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):before cropping object frame.\n", __FILE__, __LINE__); + croppedObj = lHresRGBMat(unionBox).clone(); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):after cropping object frame.\n", __FILE__, __LINE__); + // Resize object to fixed resolution, keep aspect ratio + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):before resizing object frame.\n", __FILE__, __LINE__); + resizeAspect(croppedObj, sTnWidth, sTnHeight, resizedCroppedObject); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):after resizing object frame.\n", __FILE__, __LINE__); + + payload.objFrame = resizedCroppedObject.clone(); + //resize the RGG data to required size. + cv::resize(lHresRGBMat,resizedRGBMat,cv::Size(sTnWidth,sTnHeight)); + payload.objFrame = resizedRGBMat.clone(); +#endif + //RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Resized the cropped object frame.\n", __FILE__, __LINE__); + //cv::resize(croppedObj, resizedRGBMat,cv::Size(sTnWidth, sTnHeight)); + strcpy(currSTN.fname, currSTNFname); + + snprintf(fPath, sizeof(fPath), "%s/%s", STN_PATH, currSTNFname); + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):smart thumbnail full path:%s .\n", __FILE__, __LINE__, fPath); +#ifdef USE_FILE_UPLOAD + //Write smart thumbnail to file. + //imwrite(fPath, resizedRGBMat); + imwrite(fPath, croppedObj); +#endif + + ret = STH_SUCCESS; + } + + ofData.maxBboxObjYUVFrame.release(); + lHresRGBMat.release(); + croppedObj.release(); + resizedRGBMat.release(); + + // else { + // RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Nothing to save.\n", __FILE__, __LINE__); + // ret = STH_NO_SAVE; + //} + //Release lock + //lock.unlock(); + //} + + return ret; +} + +/** @description: Logs the smart thumbnail list to be uploaded. + * @param[in] : void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +void SmartThumbnail::printSTNList() +{ + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):STN list size:%d\n", __FILE__, __LINE__, STNList.size()); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):STN fname -> STN tstamp", __FILE__, __LINE__); + //vector::iterator it; + for (vector::iterator it = STNList.begin(); it != STNList.end(); ++it) { + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): %s -> %llu \n", __FILE__, __LINE__, (*it).fname, (*it).tstamp); + } +} + +/** @description: adds smart thumbnail to list for uploading. + * @param[in] : void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::addSTN() +{ + char stnFname[256] ={0}; + + std::unique_lock lock(stnMutex); + if(STNList.size() == STN_MAX_PAYLOAD_COUNT) { + //delete the files in the list + int ctr = 0; + while(ctr < STN_MAX_PAYLOAD_COUNT) { + vector::iterator it = STNList.begin(); + memset(stnFname, 0 , sizeof(stnFname)); + snprintf(stnFname, sizeof(stnFname), "%s/%s", STN_PATH, (*it).fname); + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","addSTN %s(%d) Deleting %s file, and erasing from list\n", __FUNCTION__ , __LINE__, stnFname); + unlink(stnFname); + STNList.erase(it); + ctr++; + } + } + + STNList.push_back(currSTN); + //printSTNList(); + lock.unlock(); + + return STH_SUCCESS; +} + +/** @description: deletes smart thumbnail from list for uploading. + * @param[in] : uploadFname + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::delSTN(char* uploadFname) +{ + const char* stnFname = strrchr(uploadFname, '/'); + struct stat statbuf; + + if(stnFname) { + stnFname++; + } else { + stnFname = uploadFname; + } + + std::unique_lock lock(stnMutex); + //vector::iterator it; + for (vector::iterator it = STNList.begin(); it != STNList.end(); ++it) { + if(!strcmp((*it).fname, stnFname)){ + + //delete the file first + if(stat(CACHE_SMARTTHUMBNAIL, &statbuf) < 0) { + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Deleting %s file, and erasing from list\n", __FUNCTION__ , __LINE__, uploadFname); + unlink(uploadFname); + } + else { + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Caching %s file for reference\n", __FUNCTION__ , __LINE__, uploadFname); + } + STNList.erase(it); + break; + + } + } + lock.unlock(); + return STH_SUCCESS; +} + +/** @description: creates the payload STN list for uploading. + * @param[in] : void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::createPayload(char* uploadFname) +{ + STH_STATUS ret = STH_NO_PAYLOAD; + + std::unique_lock lock(stnMutex); + if( STNList.size() != 0) { + for (vector::iterator it = STNList.begin(); it != STNList.end(); ++it) { + if(!strcmp((*it).fname, uploadFname)){ + memset(&payload, 0 , sizeof(STHPayload)); + memcpy(&payload, &(*it), sizeof(STHPayload)); + ret = STH_SUCCESS; + RDK_LOG(RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) payload.fname:%s payload.tstamp :%llu \n", __FUNCTION__ , __LINE__, payload.fname,payload.tstamp); + break; + } + } + } + + lock.unlock(); + return ret; +} + +/** @description: get upload status. + * @param[in] : void + * @return: true if payload is ready, false otherwise + */ +bool SmartThumbnail::getUploadStatus() +{ + bool status = false; + + { + std::unique_lock lock(uploadMutex); + cv.wait(lock, [this] {return (uploadReady || termFlag);}); + + if(!termFlag) { + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Wait over due uploadReady flag!!\n",__FUNCTION__,__LINE__); + status = uploadReady; + uploadReady = false; + } else { + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Wait over due term flag!!\n",__FUNCTION__,__LINE__); + status = false; + } + + lock.unlock(); + } + + return status; +} + +/** @description: set upload status. + * @param[in] : status + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::setUploadStatus(bool status) +{ + { + std::unique_lock lock(uploadMutex); + uploadReady = status; + lock.unlock(); + } + + cv.notify_one(); + return STH_SUCCESS; +} + +/** @description: find the actual centroid of the bounding rectangle + * @param[in] bounding rectangle + * @return: central point {x,y} + */ +cv::Point2f SmartThumbnail::getActualCentroid(cv::Rect boundRect) { + + cv::Point2f pts; + + int adjustFactor = 1; // multi-by-6 to re-adjust centroid + float heightPading = 0.4; + float xPoint = adjustFactor *(boundRect.x + (boundRect.width / 2)); + float yPoint = adjustFactor *(boundRect.y + (boundRect.height /2)); + + pts.x = xPoint; + pts.y = yPoint; + + return pts; +} + +/** @description: Allign the centroid of the bounding window + * @param[in] original centroid + * @param[in] original frame resolution + * @param[in] crop window size + * @return: alligned centroid + */ +cv::Point2f SmartThumbnail::alignCentroid(cv::Point2f orgCenter, cv::Mat origFrame, cv::Size cropSize) { + + cv::Point2f pts; + + float shiftX = (orgCenter.x + (cropSize.width/2)) - origFrame.cols; + float adjustedX = orgCenter.x - STN_MAX(0, shiftX); + float shiftXleft = (adjustedX - (cropSize.width/2)); + float adjustedXfinal = adjustedX - STN_MIN(0, shiftXleft); + float shiftY = (orgCenter.y + (cropSize.height/2)) - origFrame.rows; + float adjustedY = orgCenter.y - STN_MAX(0, shiftY); + float shiftYdown = (adjustedY - (cropSize.height/2)); + float adjustedYfinal = adjustedY - STN_MIN(0, shiftYdown); + + pts.x = adjustedXfinal; + pts.y = adjustedYfinal; + + /*cout << "\n\n Original Center { " << orgCenter.x << ", " << orgCenter.y << " } " << "Alligned Center: { " << adjustedXfinal << ", " << adjustedYfinal << " } \n"; + cout << " Cropping Resolution {W, H}: { " << cropSize.width << ", " << cropSize.height << " } " << "Original frame Resolution {W, H}: { " << origFrame.cols << ", " << origFrame.rows << " } \n"; + cout << " Intermediate Adjustments {shiftX, adjustedX, shiftXleft}: { " << shiftX << ", " << adjustedX << ", " << shiftXleft << " } \n"; + cout << " Intermediate Adjustments {shiftY, adjustedY, shiftYdown}: { " << shiftY << ", " << adjustedY << ", " << shiftYdown << " } \n\n";*/ + + return pts; +} + +/** @description: Get the bounding box ordinates related to the resolution of smart thumbnail + * @param[in] boundRect: original bounding box ordinates + * @param[in] cropSize: resolution of the cropped image + * @param[in] allignedCenter: alligned center of the bounding box + * @return: relative bounding box + */ +cv::Rect SmartThumbnail::getRelativeBoundingBox(cv::Rect boundRect, cv::Size cropSize, cv::Point2f allignedCenter) { + + cv::Rect newBBox; // to store the new bounding box co-ordinate + + // to find the relative x-ordinate and width of the bounding box in the smart thumbnail image + if (boundRect.width >= cropSize.width) { + newBBox.x = 0; + newBBox.width = cropSize.width; // restrict the width of the relative bounding box to width of the final cropped image + } + else { + float deltaX = allignedCenter.x - boundRect.x; + newBBox.x = cropSize.width/2 - deltaX; + newBBox.width = boundRect.width; + } + + // to find the relative y-ordinate and height of the bounding box in the smart thumbnail image + if (boundRect.height >= cropSize.height) { + newBBox.y = 0; + newBBox.height = cropSize.height; // restrict the height of the relative bounding box to height of the final cropped image + } + else { + float deltaY = allignedCenter.y - boundRect.y; + newBBox.y = cropSize.height/2 - deltaY; + newBBox.height = boundRect.height; + } + + return newBBox; +} + +/** @description: find the crop size window {w, h} + * @param[in] bounding rectangle + * @param[in] minimum width of the crop window + * @param[in] minimum height of the crop window + * @return: size of the crop window + */ +cv::Size SmartThumbnail::getCropSize(cv::Rect boundRect,double w,double h) { + int newWidth = 0; + int newHeight = 0; + int adjustFactor = 1; + + cv::Size sz; + + newWidth = boundRect.width; + newHeight = boundRect.height; + +#if 0 + if (boundRect.width > (boundRect.height *(w/h))) { + newWidth = boundRect.width; + } else { + newWidth = (int)(boundRect.height *(w/h)); + } + + if (boundRect.height > (boundRect.width *(h/w))) { + newHeight = boundRect.height; + } else { + newHeight = (int)(boundRect.width * (h/w)); + } +#endif +#if 0 + // always ensure the minimum size of the crop size window is {w, h} + sz.height = STN_MAX(h,(newHeight* adjustFactor)); + sz.width = STN_MAX(w,(newWidth* adjustFactor)); +#endif + sz.height = h; + sz.width = w; + return sz; +} + +STH_STATUS SmartThumbnail::notify( const char* status) +{ + if(!status) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Trying to use invalid memory location!! \n", __FUNCTION__ , __LINE__); + return STH_ERROR; + } + + rtMessage req; + rtMessage_Create(&req); + rtMessage_SetString(req, "status", status); + + rtError err = rtConnection_SendMessage(smartThInst -> connectionSend, req, "RDKC.SMARTTN.STATUS"); + rtLog_Debug("SendRequest:%s", rtStrError(err)); + + if (err != RT_OK) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Error sending msg via rtmessage\n", __FILE__,__LINE__); + } + rtMessage_Release(req); + + return STH_SUCCESS; +} + +/** @description: destroy the instance of smart thumbnail + * @param[in] : void + * @return : STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::destroy() +{ + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) destroy smart thumbnail!!!\n", __FUNCTION__ , __LINE__ ); + + //Exit the rtmessage receive and upload thread. + //rtmessageSTHThreadExit = true; + //uploadSTHThreadExit = true; + + if (hres_frame_info) { + free(hres_frame_info); + hres_frame_info = NULL; + } + + //Delete the smart thumbnail instance + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Deleting the smart thumbnail instance!!!\n", __FUNCTION__ , __LINE__); + if (smartThInst) { + delete smartThInst; + smartThInst =NULL; + } + +#ifdef LEGACY_CFG_MGR + config_release(); +#endif + + return STH_SUCCESS; +} + +/** @description : Callback function to generate smart thumbnail based on motion. + * @param[in] hdr : constant pointer rtMessageHeader + * @param[in] buff : constant pointer uint8_t + * @param[in] n : uint32_t + * @param[in] closure : void pointer + * @return: void + */ +void SmartThumbnail::onMsgRefreshTnUpload(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + char const* status = NULL; + + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + char* tempbuff = NULL; + uint32_t buff_length = 0; + + rtMessage_ToString(req, &tempbuff, &buff_length); + rtLog_Debug("Req : %.*s", buff_length, tempbuff); + free(tempbuff); + + rtMessage_GetString(req, "status", &status); + + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.CVR","(%s):%d status:%s\n", __FUNCTION__, __LINE__, status); + + if(!strcmp(status, "refresh")) { + tnUploadConfRefreshed = true; + } else { + tnUploadConfRefreshed = false; + } + + rtMessage_Release(req); +} + +/** @description: Callback function for CVR clip gen status + * @param[in] hdr : pointer to rtMessage Header + * @param[in] buff : buffer for data received via rt message + * @param[in] n : number of bytes received + * @return: void + */ +void SmartThumbnail::onMsgCvr(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + int clipGenStatus = -1; + char const* cvrClipFname = NULL; + char const* cvrEvtTstamp = NULL; + uint64_t cvrEventTS = 0; + const char* delimPos = 0; + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + rtMessage_GetInt32(req, "clipStatus", &clipGenStatus); + rtMessage_GetString(req, "clipname" , &cvrClipFname); + rtMessage_GetString(req, "eventTimeStamp" , &cvrEvtTstamp); + + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d CVR clip status: %d\n", __FUNCTION__, __LINE__, clipGenStatus); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d CVR clip name: %s\n", __FUNCTION__, __LINE__, cvrClipFname); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d CVR event timestamp: %s\n", __FUNCTION__, __LINE__, cvrEvtTstamp); + + //take action on clip gen status + if(clipGenStatus == CVR_CLIP_GEN_START) { + memset(smartThInst->currSTNFname, 0, sizeof(smartThInst->currSTNFname)); + strcpy(smartThInst->currSTNFname, cvrClipFname); + strcat(smartThInst->currSTNFname, ".jpg"); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d STN fname to be generated: %s\n", __FUNCTION__, __LINE__, smartThInst->currSTNFname); + smartThInst -> cvrClipGenStarted = true; + smartThInst->logMotionEvent = true; + RDK_LOG(RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Resetting the logMotionEvent to true \n", __FILE__ , __LINE__); + + //reset payload flag + smartThInst->isPayloadAvailable = false; + smartThInst->maxBboxArea = 0; + + } else if(clipGenStatus == CVR_CLIP_GEN_END) { + + if(!smartThInst->cvrClipGenStarted) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d \t2. Recevied CVR_CLIP_GEN_END before CVR_CLIP_GEN_START.\n", __FUNCTION__, __LINE__); + } + + if((smartThInst->cvrClipGenStarted) && + (smartThInst->isPayloadAvailable) && + // to check if motion in the clip + (0 != strcmp("TIMESTAMP_NOT_AVAILABLE", cvrEvtTstamp))) { + //save smart thumbnail from memory to file + smartThInst->saveSTN(); + smartThInst->addSTN(); + smartThInst->cvrClipGenStarted = false; + + std::istringstream iss(cvrEvtTstamp); + iss >> cvrEventTS; + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Motion Timestamp: %llu\n", __FUNCTION__, __LINE__, cvrEventTS); + iss.clear(); + smartThInst->motion_time = cvrEventTS; + } + + if(!smartThInst->isPayloadAvailable) { + RDK_LOG(RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d \t3. Smart Thumbnail is not available during current period.\n", __FUNCTION__, __LINE__); + } + + if(0 == strcmp("TIMESTAMP_NOT_AVAILABLE", cvrEvtTstamp)) { + RDK_LOG(RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d \t4. Motion Timestamp is not available during current period.\n", __FUNCTION__, __LINE__); + } + + + } else { + //RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d Ignoring CVR clip gen status.\n", __FUNCTION__, __LINE__); + //RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d Possible Reason:\n", __FUNCTION__, __LINE__); + + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d \t1. Invalid Clip Gen status.\n", __FUNCTION__, __LINE__); + } + + rtMessage_Release(req); +} + +/** @description: Callback function for CVR clip upload status + * @param[in] hdr : pointer to rtMessage Header + * @param[in] buff : buffer for data received via rt message + * @param[in] n : number of bytes received + * @return: void + */ +void SmartThumbnail::onMsgCvrUpload(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + int clipUploadStatus = 0; + char const* cvrClipFname = NULL; + const char* delimPos = 0; + int createPayloadStatus = STH_NO_PAYLOAD; + char stnFname[256] = {0}; + + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + rtMessage_GetInt32(req, "status", &clipUploadStatus); + rtMessage_GetString(req, "uploadFileName", &cvrClipFname); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d CVR clip upload status: %d\n", __FUNCTION__, __LINE__, clipUploadStatus); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d CVR clip upload file: %s\n", __FUNCTION__, __LINE__, cvrClipFname); + + memset(smartThInst->uploadFname, 0, sizeof(smartThInst->uploadFname)); + //delimPos = strchr(cvrClipFname, '.'); + //strncpy(uploadFname, cvrClipFname, delimPos-cvrClipFname+1); + strcpy(smartThInst->uploadFname, cvrClipFname); + strcat(smartThInst->uploadFname, ".jpg"); + snprintf(stnFname, sizeof(stnFname), "%s/%s", STN_PATH, smartThInst->uploadFname); + + //take action on clip upload status + if(clipUploadStatus == CVR_UPLOAD_OK) { + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d Generating payload, CVR clip %s upload success.\n", __FUNCTION__, __LINE__, cvrClipFname); + createPayloadStatus = smartThInst->createPayload(smartThInst->uploadFname); + if(STH_SUCCESS == createPayloadStatus) { + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d payload created for CVR clip :%s.\n", __FUNCTION__, __LINE__, cvrClipFname); + smartThInst->setUploadStatus(true); + } else if (STH_NO_PAYLOAD == createPayloadStatus) { + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d No payload available for CVR clip:%s.\n", __FUNCTION__, __LINE__, cvrClipFname); + } else { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d invalid create payload status.\n", __FUNCTION__, __LINE__); + } + } else if(clipUploadStatus >= CVR_UPLOAD_FAIL && + clipUploadStatus <= CVR_UPLOAD_CURL_ERR) { + smartThInst->delSTN(stnFname); + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d Discard STN, CVR clip %s falied to upload.\n", __FUNCTION__, __LINE__, cvrClipFname); + } else { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","(%s):%d Unknown CVR clip upload status.\n", __FUNCTION__, __LINE__); + } + + rtMessage_Release(req); +} + +/** @description: Callback function for dynamic logging + * @param[in] hdr : pointer to rtMessage Header + * @param[in] buff : buffer for data received via rt message + * @param[in] n : number of bytes received + * @return: void + */ +void SmartThumbnail::dynLogOnMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + char const* module = NULL; + char const* logLevel = NULL; + + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + //Handle the rtmessage request + if (rtMessageHeader_IsRequest(hdr)) { + char* tmp_buff = NULL; + uint32_t tmp_buff_length = 0; + + rtMessage_ToString(req, &tmp_buff, &tmp_buff_length); + rtLog_Info("Req : %.*s", tmp_buff_length, tmp_buff); + free(tmp_buff); + + rtMessage_GetString(req, "module", &module); + rtMessage_GetString(req, "logLevel", &logLevel); + + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.DYNAMICLOG","(%s):%d Module name: %s\n", __FUNCTION__, __LINE__, module); + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.DYNAMICLOG","(%s):%d log level: %s\n", __FUNCTION__, __LINE__, logLevel); + + RDK_LOG_ControlCB(module, NULL, logLevel, 1); + + // create response + rtMessage res; + rtMessage_Create(&res); + rtMessage_SetString(res, "reply", "Success"); + rtConnection_SendResponse(con, hdr, res, 1000); + rtMessage_Release(res); + } + rtMessage_Release(req); +} + +/** @description : Callback function to capture high resolution frame + * @param[in] hdr : constant pointer rtMessageHeader + * @param[in] buff : constant pointer uint8_t + * @param[in] n : uint32_t + * @param[in] closure : void pointer + * @return: void + */ +void SmartThumbnail::onMsgCaptureFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + int processPID = -1; + int ret = STH_ERROR; + char const* strFramePTS = NULL; + uint64_t lResFramePTS = 0; + uint64_t hResFramePTS = 0; + struct timespec currTime; + + //clock the current time + memset (&currTime, 0, sizeof(struct timespec)); + clock_gettime(CLOCK_REALTIME, &currTime); + +#if 0 + //RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) time-spent:%lu \n", __FUNCTION__ , __LINE__, currTime.tv_nsec - va_msg_prev_time); + //va_msg_prev_time = currTime.tv_nsec; +#endif + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) strFramePTS:%s \n", __FUNCTION__ , __LINE__, strFramePTS); + + // read the message received + (void) closure; + rtMessage m; + rtMessage_FromBytes(&m, buff, n); + rtMessage_GetInt32(m, "processID", &processPID); + rtMessage_GetString(m, "timestamp", &strFramePTS); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) strFramePTS:%s \n", __FUNCTION__ , __LINE__, strFramePTS); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) capture invoked by process %d \n", __FUNCTION__ , __LINE__, processPID); + + std::istringstream iss(strFramePTS); + iss >> lResFramePTS; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): lResframePTS:%llu\n", __FUNCTION__, __LINE__, lResFramePTS); + + rtMessage_Release(m); + + + if (NULL == smartThInst -> hres_frame_info) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):High Res YUV Frame malloc error \n", __FUNCTION__ , __LINE__); + } + memset(smartThInst -> hres_frame_info, 0, sizeof(RDKC_PLUGIN_YUVInfo)); + +/* + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Last Motion time stamp: %d\n", __FUNCTION__ , __LINE__, smartThInst->motion_time); + // ignore frames and metadata for event_quiet_time + if( (currTime.tv_sec < (smartThInst->motion_time + smartThInst->event_quiet_time)) && (0 != smartThInst->motion_time) ) { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Within event quiet time, Ignoring event, time passed: %lu\n", __FUNCTION__ , __LINE__, (currTime.tv_sec-smartThInst->motion_time)); + return; + } + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Not within event quiet time, Capturing frame.\n", __FUNCTION__ , __LINE__); +*/ + + //read the 720*1280 YUV data. + ret = smartThInst -> recorder -> ReadYUVData(STN_HRES_BUFFER_ID, smartThInst -> hres_frame_info); + if( STH_SUCCESS != ret) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Error Reading High Res YUV Frame \n", __FUNCTION__, __LINE__); + smartThInst->isHresFrameReady = false; + return; + } + + hResFramePTS = smartThInst -> hres_frame_info -> mono_pts; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): hResframePTS:%llu\n", __FUNCTION__, __LINE__, hResFramePTS); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Time gap (hResFramePTS - lResframePTS):%llu\n", __FUNCTION__, __LINE__, (hResFramePTS - lResFramePTS)); + +#if 0 + if(!hres_yuvDataMemoryAllocationDone) { + smartThInst -> hres_y_height = smartThInst -> hres_frame_info->height; + smartThInst -> hres_y_width = smartThInst -> hres_frame_info->width; + smartThInst -> hres_y_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + smartThInst -> hres_uv_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + smartThInst -> hres_yuvData = (unsigned char *) malloc((smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char)); + + hres_yuvDataMemoryAllocationDone = true; + } + + //Acquire lock + std::unique_lock lock(stnMutex); + memset( smartThInst -> hres_yuvData, 0, (smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char) ); + memcpy( smartThInst -> hres_yuvData, smartThInst -> hres_frame_info->y_addr, smartThInst -> hres_y_size); + memcpy( smartThInst -> hres_yuvData + smartThInst -> hres_y_size, smartThInst -> hres_frame_info->uv_addr, smartThInst -> hres_uv_size); + //Release lock + lock.unlock(); +#endif + + //Ensure metadata is received after the frame is captured. + smartThInst -> isHresFrameReady = true; + +} + +/** @description : Callback function to generate smart thumbnail based on motion. + * @param[in] hdr : constant pointer rtMessageHeader + * @param[in] buff : constant pointer uint8_t + * @param[in] n : uint32_t + * @param[in] closure : void pointer + * @return: void + */ +void SmartThumbnail::onMsgProcessFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + double motionScore = 0.0; + int32_t eventType = 0; + int32_t boundingBoxXOrd = 0; + int32_t boundingBoxYOrd = 0; + int32_t boundingBoxHeight = 0; + int32_t boundingBoxWidth = 0; + uint64_t curr_time = 0; + char const* s_curr_time = NULL; + char const* strFramePTS = NULL; + uint64_t lResFramePTS = 0; + + int lBboxArea = 0; + +#if 0 + struct timespec currTime; + + //clock the current time + memset (&currTime, 0, sizeof(struct timespec)); + clock_gettime(CLOCK_REALTIME, &currTime); + + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) time-spent:%lu \n", __FUNCTION__ , __LINE__, currTime.tv_nsec - va_msg_prev_time); + va_msg_prev_time = currTime.tv_nsec; +#endif + + (void) closure; + + rtMessage m; + rtMessage_FromBytes(&m, buff, n); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) process frame invoked.\n", __FUNCTION__ , __LINE__); + + //read the metadata. + rtMessage_GetString(m, "timestamp", &strFramePTS); + rtMessage_GetInt32(m, "event_type", &eventType); + rtMessage_GetDouble(m, "motionScore", &motionScore); + rtMessage_GetInt32(m, "boundingBoxXOrd", &boundingBoxXOrd); + rtMessage_GetInt32(m, "boundingBoxYOrd", &boundingBoxYOrd); + rtMessage_GetInt32(m, "boundingBoxHeight", &boundingBoxHeight); + rtMessage_GetInt32(m, "boundingBoxWidth", &boundingBoxWidth); + rtMessage_GetString(m, "currentTime", &s_curr_time); + + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): strFramePTS:%s \n", __FUNCTION__ , __LINE__, strFramePTS); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): event Type: %d\n",__FILE__, __LINE__, eventType); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): motionScore: %f\n",__FILE__, __LINE__, motionScore); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxXOrd:%d\n", __FILE__, __LINE__,boundingBoxXOrd); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxYOrd:%d\n", __FILE__, __LINE__,boundingBoxYOrd); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxHeight:%d\n", __FILE__, __LINE__,boundingBoxHeight); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxWidth:%d\n", __FILE__, __LINE__,boundingBoxWidth); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): curr timestamp:%s\n", __FILE__, __LINE__,s_curr_time); + + std::istringstream iss(strFramePTS); + iss >> lResFramePTS; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): lResframePTS:%llu\n", __FUNCTION__, __LINE__, lResFramePTS); + iss.clear(); + + iss.str(s_curr_time); + iss >> curr_time; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): curr timestamp (uint64):%llu\n", __FILE__, __LINE__,curr_time); + iss.clear(); + + lBboxArea = boundingBoxWidth * boundingBoxHeight; + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Current Area : %d\n", __FILE__ , __LINE__, lBboxArea); + + if( (smartThInst->logMotionEvent == true) + && (eventType == 4) ) { + + smartThInst->logMotionEvent = false; + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Received Motion event from xvision during the current cvr interval, time received: %llu\n", __FILE__ , __LINE__, curr_time); + } + + // if motion is detected update the metadata. + if ((smartThInst->isHresFrameReady) && + //(motionScore != 0.0) && + (eventType == 4) && + (lBboxArea > smartThInst->maxBboxArea)) { + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Processing metadata .\n", __FUNCTION__ , __LINE__); + + smartThInst -> isHresFrameReady = false; + //Update the max bounding area. + smartThInst -> maxBboxArea = lBboxArea; + //{ + //Acquire lock + //std::unique_lock lock(stnMutex); + if (!(smartThInst -> isPayloadAvailable)) { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Payload will be available for this interval.\n", __FUNCTION__ , __LINE__); + //To indicate payload will there to upload + smartThInst -> isPayloadAvailable = true; + } + smartThInst -> updateObjFrameData(boundingBoxXOrd, boundingBoxYOrd, boundingBoxWidth, boundingBoxHeight, curr_time); + //lock.unlock(); + //} + + } else { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Metadata discarded .\n", __FUNCTION__ , __LINE__); + } + + rtMessage_Release(m); +} + +/** @description : resizes smart thumbnail to default height & width + * @param[in] im : cropped image + * @param[in] h : height + * @param[in] w : width + * @param[out] im_resized : resized smart thumbnail + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ + +#if 0 +STH_STATUS SmartThumbnail::resizeAspect(cv::Mat im, int w, int h, cv::Mat& im_resized) +{ + im_resized = cv::Mat(); + + if (im.empty()) { + //std::cerr << __FUNCTION__ << " : input image is empty" << std::endl; + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):input image is empty.\n", __FUNCTION__ , __LINE__); + return STH_ERROR; + } + + int new_w = im.cols; + int new_h = im.rows; + + if (((float)w/new_w) < ((float)h/new_h)) { + new_w = w; + new_h = (im.rows * w)/im.cols; + } else { + new_h = h; + new_w = (im.cols * h)/im.rows; + } + + cv::Mat resized; + cv::resize(im,resized,cv::Size(new_w, new_h)); + + // create image the size of the net layer + im_resized = cv::Mat(h,w,resized.type()); + + // embed image into im_resized image + int dx = (w-new_w)/2; + int dy = (h-new_h)/2; + + resized.copyTo(im_resized(cv::Rect(dx,dy,resized.cols, resized.rows))); + + return STH_SUCCESS; +} +#endif + +/** @description : resizes smart thumbnail to default height & width + * @param[in] : x ordinate of bounding box + * @param[in] : y ordinate of bounding box + * @param[in] : width of bounding box + * @param[in] : height of bounding box + * @return : void + */ +void SmartThumbnail::updateObjFrameData(int32_t boundingBoxXOrd,int32_t boundingBoxYOrd,int32_t boundingBoxWidth,int32_t boundingBoxHeight, uint64_t currTime) +{ + unsigned char* hres_yuvData = NULL; +#if 0 + char tmpFname[256] = {'\0'}; + cv::Mat lHresRGBMat; +#endif + ofData.boundingBoxXOrd = boundingBoxXOrd; + ofData.boundingBoxYOrd = boundingBoxYOrd; + ofData.boundingBoxWidth = boundingBoxWidth; + ofData.boundingBoxHeight = boundingBoxHeight; + ofData.currTime = currTime; + +#if 0 + snprintf(tmpFname, sizeof(tmpFname), "%llu.jpg", ofData.currTime); +#endif + + smartThInst -> hres_y_height = smartThInst -> hres_frame_info->height; + smartThInst -> hres_y_width = smartThInst -> hres_frame_info->width; + smartThInst -> hres_y_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + smartThInst -> hres_uv_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + hres_yuvData = (unsigned char *) malloc((smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char)); + + //Acquire lock + //std::unique_lock lock(stnMutex); + memset( hres_yuvData, 0, (smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char) ); + memcpy( hres_yuvData, smartThInst -> hres_frame_info->y_addr, smartThInst -> hres_y_size); + memcpy( hres_yuvData + smartThInst -> hres_y_size, smartThInst -> hres_frame_info->uv_addr, smartThInst -> hres_uv_size); + //Release lock + //lock.unlock(); + + //Full 720*1280 frame containing max bounding box + ofData.maxBboxObjYUVFrame = cv::Mat(smartThInst -> hres_frame_info -> height + (smartThInst -> hres_frame_info -> height)/2, smartThInst -> hres_frame_info -> width, CV_8UC1, hres_yuvData).clone(); + + if(hres_yuvData) { + free(hres_yuvData); + hres_yuvData = NULL; + } + +#if 0 + cv::cvtColor(ofData.maxBboxObjYUVFrame,lHresRGBMat, cv::COLOR_YUV2BGR_NV12); + cv::rectangle(lHresRGBMat, cv::Rect(ofData.boundingBoxXOrd, ofData.boundingBoxYOrd, ofData.boundingBoxWidth, ofData.boundingBoxHeight), cv::Scalar(0,0,255), 2); + + cv::putText(lHresRGBMat, (std::to_string(ofData.currTime)).c_str(), cv::Point(20, 20), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + cv::putText(lHresRGBMat, (std::to_string(ofData.boundingBoxHeight)).c_str(), cv::Point(40, 40), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + cv::putText(lHresRGBMat, (std::to_string(ofData.boundingBoxWidth)).c_str(), cv::Point(60, 60), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + cv::putText(lHresRGBMat, (std::to_string(ofData.boundingBoxWidth*boundingBoxHeight)).c_str(), cv::Point(80, 80), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + + imwrite(tmpFname, lHresRGBMat); +#endif +} + +/** @description: register callback for receiving msg via rtmessage. + * @param[in] void. + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::rtMsgInit() +{ + rtLog_SetLevel(RT_LOG_INFO); + rtLog_SetOption(rdkLog); + + rtConnection_Create(&connectionSend, "SMART_TN_SEND", "tcp://127.0.0.1:10001"); + rtConnection_Create(&connectionRecv, "SMART_TN_RECV", "tcp://127.0.0.1:10001"); + + rtConnection_AddListener(connectionRecv, "RDKC.SMARTTN.CAPTURE",onMsgCaptureFrame, NULL); + rtConnection_AddListener(connectionRecv, "RDKC.SMARTTN.METADATA",onMsgProcessFrame, NULL); + rtConnection_AddListener(connectionRecv, "RDKC.CVR.CLIP.STATUS",onMsgCvr, NULL); + rtConnection_AddListener(connectionRecv, "RDKC.CVR.UPLOAD.STATUS",onMsgCvrUpload, NULL); + rtConnection_AddListener(connectionRecv, "RDKC.CONF.TNUPLOAD.REFRESH",onMsgRefreshTnUpload, NULL); + + //Add listener for dynamic log topics + rtConnection_AddListener(connectionRecv, RTMSG_DYNAMIC_LOG_REQ_RES_TOPIC, dynLogOnMessage, connectionRecv); + + return STH_SUCCESS; +} + +/** + * @description: Convert event date and time to ISO 8601 format. + * + * @param[in]: strEvtDateTime,evtdatetimeSize, evtDateTime. + * + * @return: void + */ +void SmartThumbnail::stringifyEventDateTime(char* strEvtDateTime , size_t evtdatetimeSize, time_t evtDateTime) +{ + struct tm *tv = NULL; + + if(NULL == strEvtDateTime) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.CVRUPLOAD","%s(%d): Using invalid memory location!\n",__FILE__, __LINE__); + return; + } + + tv = gmtime(&evtDateTime); + + strftime(strEvtDateTime, evtdatetimeSize,"%FT%TZ",tv); +} + +/** + * @description: This function is used to sleep for some waitingInterval before next retry. + * + * @return: Error code. + */ +int SmartThumbnail::retryAtExpRate() +{ + int ret = RDKC_FAILURE; + int retryFactor = 2; + + if(waitingInterval <= STN_UPLOAD_TIME_INTERVAL) + { + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.CVRPOLL","%s: %d: Waiting for %d seconds!\n", __FILE__, __LINE__, (int)waitingInterval); + sleep(waitingInterval); + waitingInterval *= retryFactor; + ret = RDKC_SUCCESS; + } + return ret; +} + +/** @description: thread routine to upload smart thumbnail payload + * @param[in] : time left + * @return: void + */ +void SmartThumbnail::uploadPayload() +{ + int curlCode = 0; + long response_code = 0; + char modelName[CONFIG_STRING_MAX] = {0}; + char macAddress[CONFIG_STRING_MAX] = {0}; + char firmwareName[FW_NAME_MAX_LENGTH] = {0}; +#ifdef USE_FILE_UPLOAD + char *data = NULL; + struct stat fileStat; + int fileLen = 0; + int readLen = 0; + char *ptr = NULL; + char readBuf[STN_UPLOAD_SEND_LEN]; + int fd = 0; + char sTnUploadFpath[256] = {0}; + char sTnTStamp[256] = {0}; + //struct tm* tv = NULL; +#else + char *dataPtr = NULL; + int dataLen = 0; + //std::vector dataPtr; + //dataPtr.reserve(STN_DATA_MAX_SIZE); +#endif + char packHead[STN_UPLOAD_SEND_LEN+1]; + int retry = 0; + int remainingTime = 0; + + struct timespec currTime; + struct timespec startTime; + memset(&startTime, 0, sizeof(startTime)); + memset(&currTime, 0, sizeof(currTime)); + + setMacAddress(macAddress); + setModelName(modelName); + setCameraImageName(firmwareName); + + //clock the start time + clock_gettime(CLOCK_REALTIME, &startTime); + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Uploading smart thumbnail!!! Time left %ld\n", __FILE__, __LINE__, STN_UPLOAD_TIME_INTERVAL); + + while (true) { + //clock the current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + remainingTime = STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec); + + if ( remainingTime <= 0 ) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL", "%s(%d): Max time exceeded for stn upload after retry count %d !!!\n", __FILE__,__LINE__, retry); + break; + } + + if (0 == retry) { + std::unique_lock lock(smartThInst -> stnMutex); + snprintf(sTnUploadFpath, sizeof(sTnUploadFpath), "%s/%s", STN_PATH, smartThInst->payload.fname); + //snprintf(sTnTStamp, sizeof(sTnTStamp), "%llu", payload.tstamp); + stringifyEventDateTime(sTnTStamp, sizeof(sTnTStamp), smartThInst->payload.tstamp); + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):STN Time is %s\n", __FILE__, __LINE__, sTnTStamp); + + //check for empty payload and break if so + /*if (payload.objFrame.empty()) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):payload is empty!!!\n", __FILE__, __LINE__); + break; + }*/ +#if 0 + tv = gmtime(&currTime.tv_sec); + snprintf(uploadFname, sizeof(uploadFname), "%s/%04d%02d%02d%02d%02d%02d.jpg", STN_PATH,(tv->tm_year+1900), tv->tm_mon+1, tv->tm_mday, tv->tm_hour, tv->tm_min, tv->tm_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail upload file: %s\n",__FILE__, __LINE__, uploadFname); + //Write smart thumbnail to file. + imwrite(uploadFname, payload.objFrame); +#endif + +#ifdef USE_FILE_UPLOAD + /* get file attribute */ + if (stat(sTnUploadFpath, &fileStat) < 0) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): invalid file [%s], errmsg=%s\n",__FILE__, __LINE__, sTnUploadFpath, strerror(errno)); + lock.unlock(); + break; + /*retry++; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retry again for %d times!!!\n",__FILE__,__LINE__,retry); + continue;*/ + } + + fileLen = fileStat.st_size; + RDK_LOG(RDK_LOG_INFO, "LOG.RDK.SMARTTHUMBNAIL", "%s(%d):Length of the smart thumbnail file to be uploaded:%d\n", __FILE__, __LINE__,fileLen); + + fd = open(sTnUploadFpath, O_RDONLY); + if (fd <= 0) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to Open smart thumbnail upload File :%s : error : [%s]\n", __FILE__, __LINE__, sTnUploadFpath, strerror(errno)); + lock.unlock(); + pid_t pid = 0; + pid = getpid(); + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.CVRUPLOAD","%s(%d): Open failed sending sigterm to thread id : %d\n", __FUNCTION__, __LINE__,pid); + kill(pid, SIGTERM); + break; + /*retry++; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retrying again for %d times!!!\n",__FILE__,__LINE__,retry); + continue;*/ + } + + data =(char*)malloc(fileLen*sizeof(char)); + if (NULL == data) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to allocate memory :%s \n", __FILE__, __LINE__, sTnUploadFpath); + if (fd >= 0) { + close(fd); + } + lock.unlock(); + break; + /*retry++; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retrying again for %d times!!!\n",__FUNCTION__,__LINE__,retry); + continue;*/ + } + + memset(data,0,fileLen); + memset(readBuf,0,STN_UPLOAD_SEND_LEN); + ptr=data; + + // read thumbnail file into buffer + while((readLen = read(fd, readBuf, sizeof(readBuf))) > 0) { + memcpy(ptr, readBuf, readLen); + ptr += readLen; + memset(readBuf,0,STN_UPLOAD_SEND_LEN); + } + + lock.unlock(); +#else + dataLen = payload.objFrame.total() * payload.objFrame.elemSize(); + dataPtr = reinterpret_cast(payload.objFrame.data); + //cv::imencode(".jpg", payload.objFrame, dataPtr); + //dataLen = dataPtr.size(); +#endif + } + + /* Add Header */ + smartThInst->httpClient->resetHeaderList(); + smartThInst->httpClient->addHeader( "Expect", ""); //removing expect header condition by explicitly setting Expect header to "" + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%s", smartThInst -> smtTnAuthCode); + smartThInst->httpClient->addHeader( "Authorization", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "image/jpeg"); + smartThInst->httpClient->addHeader( "Content-Type", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "Sercomm %s %s %s", modelName, firmwareName, macAddress); + smartThInst->httpClient->addHeader( "User-Agent", packHead); + memset(packHead, 0, sizeof(packHead)); + //snprintf(packHead, sizeof(packHead), "%llu", payload.tstamp); + smartThInst->httpClient->addHeader( "X-EVENT-DATETIME", sTnTStamp); + + memset(packHead, 0, sizeof(packHead)); + //SMTN's Bounding box of Motion Detection area + snprintf(packHead, sizeof(packHead), "%d, %d, %d, %d", relativeBBox.x, relativeBBox.y, relativeBBox.width, relativeBBox.height); + smartThInst->httpClient->addHeader( "X-BoundingBox", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "CVR"); + smartThInst->httpClient->addHeader("X-VIDEO-RECORDING", packHead); + +#ifdef USE_FILE_UPLOAD + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%s", sTnUploadFpath); + smartThInst->httpClient->addHeader( "X-FILE-NAME", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%d",fileLen); + smartThInst->httpClient->addHeader( "Content-Length", packHead); + + //upload data + curlCode = smartThInst->httpClient->post_binary(smartThInst -> smtTnUploadURL, data, &response_code, fileLen, remainingTime); +#else + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%d",dataLen); + smartThInst->httpClient->addHeader( "Content-Length", packHead); + + curlCode = smartThInst->httpClient->post_binary(smartThInst -> smtTnUploadURL, dataPtr, &response_code, dataLen, remainingTime); +#endif + if ((response_code >= RDKC_HTTP_RESPONSE_OK) && (response_code < RDKC_HTTP_RESPONSE_REDIRECT_START)) + { + break; + } else { + retry++; + if(RDKC_SUCCESS != retryAtExpRate()) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.CVRUPLOAD","%s(%d): retries done with current exp wait interval %d\n",__FILE__, __LINE__,waitingInterval); + break; + } + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retrying again for %d times with %d sec remaining timeout because last try failed with response code %lu and curl code %d\n",__FUNCTION__,__LINE__,retry, remainingTime, response_code, curlCode); + } + } + + //log success/failure for telemetry + if ((response_code >= RDKC_HTTP_RESPONSE_OK) && (response_code < RDKC_HTTP_RESPONSE_REDIRECT_START)) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart Thumbnail uploaded successfully with header X-EVENT-TIME: %s X-BoundingBox: %d %d %d %d X-VIDEO-RECORDING:CVR\n",__FUNCTION__,__LINE__,sTnTStamp, relativeBBox.x, relativeBBox.y, relativeBBox.width, relativeBBox.height); + }else { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart Thumbnail upload failed with response code %lu and curl code %d\n",__FUNCTION__,__LINE__, response_code, curlCode); + } + +#ifdef USE_FILE_UPLOAD + if(NULL != data) { + free(data); + data = NULL; + } + + if (fd >= 0) { + close(fd); + } + RDK_LOG( RDK_LOG_TRACE1,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Removing smart thumbnail upload file: %s\n",__FILE__, __LINE__, sTnUploadFpath); + //unlink (uploadFname); + delSTN(sTnUploadFpath); +#endif +} + + +/** @description : thread routine to listen to messages + * @param[in] : void. + * @return : void + */ +STH_STATUS SmartThumbnail::receiveRtmessage() +{ + rtError err; + + while (!termFlag) { + err = rtConnection_Dispatch(connectionRecv); + if (err != RT_OK) { + //rtLog_Debug("dispatch:%s", rtStrError(err)); + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): dispatch:%s\n",__FUNCTION__,__LINE__,rtStrError(err)); + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error receiving msg via rtmessage\n",__FUNCTION__,__LINE__); + } + usleep(10000); + } + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exit rtMessage listening loop .\n",__FUNCTION__,__LINE__); + cv.notify_one(); +} + +void SmartThumbnail::uploadSTN() +{ + while (!termFlag){ + //exit app if smt Thumbnail is disabled via RFC + /*if(!smartThInst->checkEnabledRFCFeature(RFC_SMART_TN_UPLOAD, SMART_TN_UPLOAD)) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exiting smart thumbnail, Disable via RFC!!!\n", __FILE__, __LINE__); + break; + }*/ + if(tnUploadConfRefreshed){ + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Tn Upload Conf refreshed, fetching new URL and auth token!!\n", __FUNCTION__, __LINE__); + + smartThInst->httpClient->close(); + + memset(smartThInst->smtTnUploadURL, 0 , sizeof(smartThInst->smtTnUploadURL)); + memset(smartThInst->smtTnAuthCode, 0 , sizeof(smartThInst->smtTnAuthCode)); + + if(STH_SUCCESS == smartThInst -> getTnUploadConf()) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): getTnUploadConf success!!", __FUNCTION__, __LINE__); + } + + /* Open the URL */ + if (NULL != smartThInst->httpClient) { + //smartThInst->httpClient->open(smartThInst->smtTnUploadURL,smartThInst -> dnsCacheTimeout, STN_MAX_UPLOAD_TIMEOUT); + smartThInst->httpClient->open(smartThInst->smtTnUploadURL,smartThInst -> dnsCacheTimeout); + } else { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to open the URL\n", __FUNCTION__, __LINE__); + } + + tnUploadConfRefreshed = false; + } + + if(smartThInst -> getUploadStatus()) { + smartThInst->uploadPayload(); + } + } + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exiting smart thumbnail upload thread.\n",__FUNCTION__,__LINE__); +} + +/** + * @description: This function is used to get the camera firmware version. + * + * @param[out]: void + * + * @return: Error code. + */ +int SmartThumbnail::setCameraImageName(char *out) +{ + size_t max_line_length = FW_NAME_MAX_LENGTH; + char *file_buffer; + char *locate_1 = NULL; + FILE* fp; + char* temp = out; + + fp = fopen("/version.txt","r"); + if (NULL == fp) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAILUPLOAD","%s(%d): Error in opening version.txt \n", __FILE__, __LINE__); + return RDKC_FAILURE; + } + + file_buffer = (char*)malloc(FW_NAME_MAX_LENGTH + 1); + if(file_buffer == NULL) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAILUPLOAD","%s(%d): Error in malloc \n", __FILE__, __LINE__); + fclose(fp); + return RDKC_FAILURE; + } + + while(getline(&file_buffer,&max_line_length,fp) != -1) + { + /* find the imagename string */ + locate_1 = strstr(file_buffer,"imagename"); + if(locate_1) + { + locate_1 += strlen("imagename:"); + /* copy the contents till linefeed */ + while(*locate_1 != '\n') + *out++ = *locate_1++; + free(file_buffer); + file_buffer = NULL; + fclose(fp); + return RDKC_SUCCESS; + } + } + + /* unable to get the image name */ + strcpy(out,"imagename entry not found"); + free(file_buffer); + file_buffer = NULL; + fclose(fp); + return RDKC_SUCCESS; +} + +/** + * @description: This function is used to set the camera firmware version. + * @param[out]: void + * @return: Error code. +* */ +int SmartThumbnail::setModelName(char* modelName) +{ +#ifdef USE_MFRLIB + mfrSerializedData_t stdata = {NULL, 0, NULL}; + mfrSerializedType_t stdatatype = mfrSERIALIZED_TYPE_MODELNAME; + char mac[CONFIG_STRING_MAX]; + + if (mfrGetSerializedData(stdatatype, &stdata) == mfrERR_NONE) { + strncpy(modelName,stdata.buf,stdata.bufLen); + modelName[stdata.bufLen] = '\0'; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Model Name = %s,%s,%d\n",__FILE__, __LINE__,modelName,stdata.buf,stdata.bufLen); + if (stdata.freeBuf != NULL) { + stdata.freeBuf(stdata.buf); + stdata.buf = NULL; + } + } + else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):GET ModelName failed : %d\n", __FILE__, __LINE__); + } +#endif +} + +/** + * @description: This function is used to set the camera's mac address. + * @param[out]: void + * @return: Error code. +* */ +int SmartThumbnail::setMacAddress(char* macAddress) +{ + char mac[CONFIG_STRING_MAX] = {0}; + +#ifdef USE_MFRLIB + mfrSerializedData_t stdata = {NULL, 0, NULL}; + mfrSerializedType_t stdatatype = mfrSERIALIZED_TYPE_DEVICEMAC; + + if (mfrGetSerializedData(stdatatype, &stdata) == mfrERR_NONE) { + strncpy(mac,stdata.buf,stdata.bufLen); + mac[stdata.bufLen] = '\0'; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):mac= %s,%s,%d\n",__FILE__, __LINE__,mac,stdata.buf,stdata.bufLen); + + char tmpMac[CONFIG_STRING_MAX+1] = {0}; + char *tmpField; + int fieldNum = 0; + + strcpy(tmpMac, mac); + tmpField = strtok(tmpMac, ":"); + + while (tmpField != NULL && fieldNum < 6) { + char *chk; + unsigned long tmpVal; + + tmpVal = strtoul(tmpField, &chk, 16); + + if (tmpVal > 0xff) { + RDK_LOG( RDK_LOG_WARN,"LOG.RDK.SMARTTHUMBNAIL","field %d value %0x out of range\n", fieldNum, tmpVal); + } + if (*chk != 0) { + RDK_LOG( RDK_LOG_WARN,"LOG.RDK.SMARTTHUMBNAIL","Non-digit character %c (%0x) detected in field %d\n", *chk, *chk, fieldNum); + + } + fieldNum++; + strcat(macAddress, tmpField); + tmpField = strtok(NULL, ":"); + } + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):mac address= %s\n",__FILE__, __LINE__,macAddress); + if (stdata.freeBuf != NULL) { + stdata.freeBuf(stdata.buf); + stdata.buf = NULL; + } + + } + else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):GET MAC failed : %d\n", __FILE__, __LINE__); + } +#endif +} diff --git a/smart_thumbnail_cvr_lite/Makefile b/smart_thumbnail_cvr_lite/Makefile new file mode 100644 index 0000000..35b1204 --- /dev/null +++ b/smart_thumbnail_cvr_lite/Makefile @@ -0,0 +1,70 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +# Add dependent libraries +USE_OPENCV = yes +USE_HTTPCLIENT = yes +USE_CONFIGMGR = yes +USE_RTMESSAGE = yes +USE_PLUGINS = yes + +include ${RDK_PROJECT_ROOT_PATH}/utility/AppsRule.mak +LIBS = $(LIBFLAGS) + +USE_LEGACY_CONFIG_MGR = no + +ifeq ($(XCAM_MODEL), SCHC2) +CFLAGS += -DXCAM2 +endif + +CFLAGS += -I./include +CFLAGS += -std=c++14 -fPIC -Wall -Wextra + +CFLAGS += -DUSE_FILE_UPLOAD + +ifeq ($(USE_LEGACY_CONFIG_MGR), yes) +CFLAGS += -DLEGACY_CFG_MGR +endif + +CFLAGS += -DUSE_MFRLIB + +SRC += smart_thumbnail.cpp main.cpp +OBJ = $(SRC:.cpp=.o) + +TARGET = smartthumbnail_cvr_lite + +all: $(TARGET) + +RM = rm +INSTALL = install + +$(TARGET): $(OBJ) + $(CXX) -o $(@) $^ $(LIBS) + +%.o:%.cpp + $(CXX) -c $< $(CFLAGS) -o $@ + +install: + $(INSTALL) -D $(TARGET) ${RDK_SDROOT}/usr/local/bin/$(TARGET) + +uninstall: + $(RM) -f $(RDK_SDROOT)/usr/local/bin/$(TARGET) + +clean: + $(RM) -rf $(TARGET) $(OBJ) diff --git a/smart_thumbnail_cvr_lite/include/smart_thumbnail.h b/smart_thumbnail_cvr_lite/include/smart_thumbnail.h new file mode 100644 index 0000000..98589bf --- /dev/null +++ b/smart_thumbnail_cvr_lite/include/smart_thumbnail.h @@ -0,0 +1,214 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +*/ +#ifndef __SMART_THUMBNAIL_H__ +#define __SMART_THUMBNAIL_H__ + +#include +#include +#include +#include +#include +#include + +#ifdef LEGACY_CFG_MGR +#include "dev_config.h" +#else +#include "polling_config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef USE_MFRLIB +#include "mfrApi.h" +#endif +#ifdef __cplusplus +} +#endif + +#include "rdk_debug.h" +#include "rtConnection.h" +#include "rtLog.h" +#include "rtMessage.h" +#include "HttpClient.h" +#include "RdkCVideoCapturer.h" +#include "RdkCPluginFactory.h" +#include "RFCCommon.h" +#include "opencv2/opencv.hpp" + + +#define FW_NAME_MAX_LENGTH 512 +#define CONFIG_STRING_MAX (256) +#define YUV_HRES_BUFFER_ID 0 +#define YUV_HRES_FRAME_WIDTH 1280 +#define YUV_HRES_FRAME_HEIGHT 720 + +#ifndef XCAM2 +#define STN_HRES_BUFFER_ID 0 +#else +#define STN_HRES_BUFFER_ID 2 +#endif + +//actual width and height of smart thumbnail to be uploaded +#define STN_FRAME_WIDTH 212 +#define STN_FRAME_HEIGHT 119 + +#define STN_DEFAULT_DNS_CACHE_TIMEOUT 60 + +//default width and height of smart thumbnail +#define STN_DEFAULT_WIDTH 640 +#define STN_DEFAULT_HEIGHT 480 + +#define STN_TIMESTAMP_TAG "timestamp" +#define STN_UPLOAD_TIME_INTERVAL 15 + +#define STN_PATH "/tmp" +#define STN_UPLOAD_SEND_LEN 2048 +#define STN_MAX_RETRY_COUNT 3 +#define STN_COMPRESSION_SCALE 40 +#define STN_DATA_MAX_SIZE 1024*1024 //1 MB +#define STN_TSTAMP_SIZE 50 +#define STN_DEFAULT_EVT_QUIET_TIME 30 + +#define STN_TRUE "true" +#define STN_FALSE "false" + +#define RTMSG_DYNAMIC_LOG_REQ_RES_TOPIC "RDKC.ENABLE_DYNAMIC_LOG" + +#define STN_MAX( a, b ) ( ( a > b) ? a : b ) +#define STN_MIN( a, b ) ( ( a < b) ? a : b ) + +typedef enum { + STH_ERROR = -1, + STH_SUCCESS, + STH_NO_PAYLOAD +}STH_STATUS; + +typedef struct { + cv::Mat objFrame; + uint64_t tstamp; +}STHPayload; + +typedef struct { + uint32_t boundingBoxXOrd; + uint32_t boundingBoxYOrd; + uint32_t boundingBoxWidth; + uint32_t boundingBoxHeight; + cv::Mat maxBboxObjYUVFrame; + uint64_t currTime; +}objFrameData; + +class SmartThumbnail +{ + public: + static SmartThumbnail* getInstance(); + //Initialize the buffers and starts msg monitoring, upload thread. + STH_STATUS init(); + //Pushes the data to the upload queue at the end of interval. + STH_STATUS createPayload(); + //Upload smart thumbnail data + void uploadPayload(time_t timeLeft); + //notify start or end of smart thumbnail process + STH_STATUS notify(const char* status); + //call Smart thumbnail destructor and deallocates dynamic allocated memory. + STH_STATUS destroy(); + + private: + SmartThumbnail(); + ~SmartThumbnail(); + STH_STATUS getTnUploadConf(); + STH_STATUS getEventConf(); + //sets the camera firmware version. + int setCameraImageName(char* out); + //sets the camera firmware version. + int setModelName(); + //sets the camera's mac address. + int setMacAddress(); + // registers Callback for rtmessage + STH_STATUS rtMsgInit(); + //Read the High resolution YUV frame. + static void onMsgCaptureFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + //Generate the RGB object detection frame. + static void onMsgProcessFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + + //Updates object frame + static void updateObjFrameData(int32_t boundingBoxXOrd,int32_t boundingBoxYOrd,int32_t boundingBoxWidth,int32_t boundingBoxHeight,uint64_t currTime); + + //Resize the cropped area keeping the aspect ratio. + STH_STATUS resizeAspect(cv::Mat im, int w, int h, cv::Mat& im_resized); + + //Thread routine to receive data + static void receiveRtmessage(); + //Callback function for dynamic logging. + static void dynLogOnMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + + cv::Point2f getActualCentroid(cv::Rect boundRect); + cv::Point2f alignCentroid(cv::Point2f orgCenter, cv::Mat origFrame, cv::Size cropSize); + cv::Size getCropSize(cv::Rect boundRect,double w,double h); + cv::Rect getRelativeBoundingBox(cv::Rect boundRect, cv::Size cropSize, cv::Point2f allignedCenter); + + static SmartThumbnail* smartThInst; + RdkCPluginFactory* pluginFactory; + int g_hres_buf_id; + RdkCVideoCapturer* recorder; + RDKC_PLUGIN_YUVInfo* hres_frame_info; + bool hres_yuvDataMemoryAllocationDone; + + std::thread rtMessageReceive; + std::thread uploadThread; + bool rtmessageSTHThreadExit; + bool isPayloadAvailable; + std::mutex QMutex; + rtConnection connectionRecv; + rtConnection connectionSend; + rtError err; + int maxBboxArea; + objFrameData ofData; + unsigned char* hres_yuvData; + unsigned char* hres_y_data; + unsigned char* hres_uv_data; + int hres_y_size; + int hres_y_height; + int hres_y_width; + int hres_uv_size; + bool isHresFrameReady; + + HttpClient* httpClient; + int dnsCacheTimeout; + STHPayload payload; + + uint64_t prev_time; + int32_t event_quiet_time; + + char smtTnUploadURL[CONFIG_STRING_MAX]; + char smtTnAuthCode[AUTH_TOKEN_MAX]; + char modelName[CONFIG_STRING_MAX]; + char macAddress[CONFIG_STRING_MAX]; + char firmwareName[FW_NAME_MAX_LENGTH]; + + int sTnHeight; + int sTnWidth; + char uploadFname[256]; + cv::Rect relativeBBox; +}; + +#endif //__SMART_THUMBNAIL_H__ diff --git a/smart_thumbnail_cvr_lite/main.cpp b/smart_thumbnail_cvr_lite/main.cpp new file mode 100644 index 0000000..be2c258 --- /dev/null +++ b/smart_thumbnail_cvr_lite/main.cpp @@ -0,0 +1,173 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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 "smart_thumbnail.h" +#include + +#if 0 +/** @description: Checks if the feature is enabled via RFC + * @param[in] rfc_feature_fname: RFC feature filename + * @param[in] feature_name: RFC parameter name + * @return: bool + */ +bool checkEnabledRFCFeature(char* rfcFeatureFname, char* featureName) +{ + /* set cvr audio through RFC files */ + char value[10] = {0}; + + if((NULL == rfcFeatureFname) || + (NULL == featureName)) { + return STN_FALSE; + } + + /* Check if RFC configuration file exists */ + if(0 == IsRFCFileAvailable(rfcFeatureFname)) { + /* Get the value from RFC file */ + if( STH_SUCCESS == GetValueFromRFCFile(rfcFeatureFname, featureName, value) ) { + if( strcmp(value, STN_TRUE) == 0) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): %s is enabled via RFC.\n",__FILE__, __LINE__, featureName); + return true; + } else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDKSMARTTHUMBNAIL","%s(%d): %s is disabled via RFC.\n",__FILE__, __LINE__, featureName); + return false; + } + } + /* If RFC file is not present, disable the feature */ + } else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): rfc feature file %s is not present.\n",__FILE__, __LINE__, rfcFeatureFname); + return false; + } +} +#endif + +int main(int argc, char** argv) +{ + SmartThumbnail *smTnInstance = NULL; + STH_STATUS status = STH_SUCCESS; + time_t remainingTime = 0; + + struct timespec currTime; + struct timespec startTime; + memset(&startTime, 0, sizeof(startTime)); + memset(&currTime, 0, sizeof(currTime)); + + //initialize rdklogger + rdk_logger_init("/etc/debug.ini"); + //initialize RFC + RFCConfigInit(); + + //create instance + smTnInstance = SmartThumbnail::getInstance(); + if (!smTnInstance) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating Smart thumbnail instance.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + //initialize smart thumbnail + status = smTnInstance-> init(); + if (STH_ERROR == status) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating Smart thumbnail instance.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Notify xvision and cvr daemon.\n", __FILE__, __LINE__); + + //Initially sleep for upload interval time, to allow smart thumbnail to be generated. + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Putting the Smart Thumnail Upload to sleep for %d seconds.\n", __FILE__, __LINE__, STN_UPLOAD_TIME_INTERVAL); + sleep(STN_UPLOAD_TIME_INTERVAL); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart Thumbnail Upload is ready.\n", __FILE__, __LINE__); + + while (true) { + + //Notify start status + smTnInstance-> notify("start"); + + //clock the start time + memset(&startTime, 0, sizeof(startTime)); + clock_gettime(CLOCK_REALTIME, &startTime); +#if 0 + //exit app if smt Thumbnail is disabled via RFC + if(!checkEnabledRFCFeature(RFC_SMART_TN_UPLOAD, SMART_TN_UPLOAD)) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exiting smart thumbnail, Disable via RFC!!!\n", __FILE__, __LINE__); + break; + } +#endif + //create payload + status = smTnInstance->createPayload(); + if ( (STH_NO_PAYLOAD == status) || + (STH_ERROR == status) ) { + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Unable to create payload for smart thumnail, hence skipping!!!\n", __FILE__, __LINE__); + + //clock current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Time spent for payload creation %d seconds!!!\n", __FILE__, __LINE__, (currTime.tv_sec - startTime.tv_sec)); + + // sleep maximum of smart thumbnail time interval(~15 seconds) + if( (currTime.tv_sec - startTime.tv_sec) >= STN_UPLOAD_TIME_INTERVAL ) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exceed upload time interval!!!\n", __FILE__, __LINE__); + continue; + } else { + remainingTime = STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Sleep for remaining %d seconds!!!\n", __FILE__, __LINE__, remainingTime); + sleep (remainingTime); + continue; + } + } + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Created payload for smart thumnail successfully.. Going to upload now!!!\n", __FILE__, __LINE__); + + // clock the current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): currTime.tv_sec %d startTime.tv_sec %d (currTime.tv_sec - startTime.tv_sec)!!!\n", __FILE__, __LINE__, currTime.tv_sec, startTime.tv_sec, (currTime.tv_sec - startTime.tv_sec)); + + smTnInstance->uploadPayload(STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec)); + + // clock the current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Time spent to upload smart thumbnail %d seconds!!!\n", __FILE__, __LINE__, (currTime.tv_sec - startTime.tv_sec)); + if( (currTime.tv_sec - startTime.tv_sec) >= STN_UPLOAD_TIME_INTERVAL ) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Exceed upload time interval!!!\n", __FILE__, __LINE__); + continue; + } + else { + //calculate the time needed to sleep for next interval. + remainingTime = STN_UPLOAD_TIME_INTERVAL - (currTime.tv_sec - startTime.tv_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Sleep for remaining %d seconds!!!\n", __FILE__, __LINE__, remainingTime); + sleep (remainingTime); + } + } + + //notify exit status + smTnInstance -> notify("stop"); + + //destroy smartThumbnail instance + if(smTnInstance) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Deleting smart thumnail instance!!!\n", __FILE__, __LINE__); + smTnInstance-> destroy(); + } + RFCRelease(); + return 0; +} + diff --git a/smart_thumbnail_cvr_lite/smart_thumbnail.cpp b/smart_thumbnail_cvr_lite/smart_thumbnail.cpp new file mode 100644 index 0000000..f6e40d6 --- /dev/null +++ b/smart_thumbnail_cvr_lite/smart_thumbnail.cpp @@ -0,0 +1,1298 @@ +/* +i########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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 "smart_thumbnail.h" + +SmartThumbnail* SmartThumbnail::smartThInst = NULL; +#if 0 +SmartThumbnail* SmartThumbnail::smartThInst = NULL; + +bool SmartThumbnail::rtmessageSTHThreadExit = false; + +bool SmartThumbnail::isHresFrameReady = false; +bool SmartThumbnail::isPayloadAvailable = false; +HttpClient* SmartThumbnail::httpClient = NULL; + +STHPayload SmartThumbnail::payload ; +objFrameData SmartThumbnail::ofData ; + +RDKC_PLUGIN_YUVInfo *SmartThumbnail::hres_frame_info = NULL; +RdkCVideoCapturer *SmartThumbnail::recorder = NULL; +bool SmartThumbnail::hres_yuvDataMemoryAllocationDone = false; +int SmartThumbnail::hres_y_size = 0; +int SmartThumbnail::hres_uv_size = 0; +unsigned char *SmartThumbnail::hres_yuvData = NULL; + +int SmartThumbnail::hres_y_height = 0; +int SmartThumbnail::hres_y_width = 0; + +rtConnection SmartThumbnail::connectionSend; +rtConnection SmartThumbnail::connectionRecv; +rtError SmartThumbnail::err; + +uint64_t SmartThumbnail::prev_time = 0; +int SmartThumbnail::maxBboxArea = 0; + +std::mutex SmartThumbnail::QMutex; + +char SmartThumbnail::smtTnUploadURL[CONFIG_STRING_MAX] = {0}; +char SmartThumbnail::smtTnAuthCode[AUTH_TOKEN_MAX] = {0}; +char SmartThumbnail::modelName[CONFIG_STRING_MAX] = {0}; +char SmartThumbnail::macAddress[CONFIG_STRING_MAX] = {0}; +char SmartThumbnail::firmwareName[FW_NAME_MAX_LENGTH] = {0}; + +int32_t SmartThumbnail::event_quiet_time = STN_DEFAULT_EVT_QUIET_TIME; +#endif + +/** @description: Constructor + * @param[in] void + * @return: void + */ +SmartThumbnail::SmartThumbnail():dnsCacheTimeout(STN_DEFAULT_DNS_CACHE_TIMEOUT), + sTnHeight(STN_DEFAULT_HEIGHT), + sTnWidth(STN_DEFAULT_WIDTH), + rtmessageSTHThreadExit(false), + isHresFrameReady(false), + isPayloadAvailable(false), + httpClient(NULL), + hres_frame_info(NULL), + recorder(NULL), + hres_yuvDataMemoryAllocationDone(false), + hres_y_size(0), + hres_uv_size(0), + hres_yuvData(NULL), + hres_y_height(0), + hres_y_width(0), + prev_time(0), + maxBboxArea(0), + event_quiet_time(STN_DEFAULT_EVT_QUIET_TIME) +{ + memset(uploadFname, 0, sizeof(uploadFname)); + memset(smtTnUploadURL, 0, sizeof(smtTnUploadURL)); + memset(smtTnAuthCode, 0, sizeof(smtTnAuthCode)); + memset(modelName, 0, sizeof(modelName)); + memset(macAddress, 0, sizeof(macAddress)); + memset(firmwareName, 0, sizeof(firmwareName)); + + + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart thumbnail constructor invoked.\n", __FUNCTION__, __LINE__); +} + +/** @description: Destructor + * @param[in] void + * @return: void + */ +SmartThumbnail::~SmartThumbnail() +{ + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart thumbnail constructor invoked.\n", __FUNCTION__, __LINE__); +} + +/** @description: creates the instance for smart thumbnail + * @param[in] void + * @return: pointer to instance of SmartThumbnail + */ +SmartThumbnail *SmartThumbnail::getInstance() +{ + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail instance.\n", __FUNCTION__, __LINE__); + if (!smartThInst) { + smartThInst = new SmartThumbnail(); + } + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart thumbnail instance created.\n", __FUNCTION__, __LINE__); + + return smartThInst; +} + +/** @description: initialize smart thumbnail + * @param[in] void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::init() +{ + int ret= STH_SUCCESS; + RDKC_PLUGIN_YUVBufferInfo *buf_format = NULL; + char usrVal[CONFIG_STRING_MAX]; + +#ifdef LEGACY_CFG_MGR + char *configParam = NULL; + + //initializing config Manager + if (STH_SUCCESS != config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error loading config manager.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + // get upload url + memset(usrVal, 0, sizeof(usrVal)); + if (STH_SUCCESS != rdkc_get_user_setting(SMART_THUMBNAIL_UPLOAD_URL,usrVal)) { + configParam=(char*)rdkc_envGet(SMART_THUMBNAIL_UPLOAD_URL); + } else { + configParam =usrVal; + } + + if (NULL != configParam) { + strcpy(smtTnUploadURL,configParam); + configParam = NULL; + } + + // get auth code + memset(usrVal, 0, sizeof(usrVal)); + if (STH_SUCCESS != rdkc_get_user_setting(SMART_THUMBNAIL_AUTH_CODE ,usrVal)) { + configParam=(char*)rdkc_envGet(SMART_THUMBNAIL_AUTH_CODE ); + } else { + configParam =usrVal; + } + if (NULL != configParam) { + strcpy(smtTnAuthCode,configParam); + configParam = NULL; + } +#else + if(STH_SUCCESS == getTnUploadConf()) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): getTnUploadConf success!!", __FUNCTION__, __LINE__); + } + + if(STH_SUCCESS == getEventConf()) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): getEventConf success!!", __FUNCTION__, __LINE__); + } + +#endif + + // update mac/model/camera image + memset(smartThInst -> modelName, 0, sizeof(smartThInst -> modelName)); + memset(smartThInst -> macAddress, 0, sizeof(smartThInst -> macAddress)); + memset(smartThInst -> firmwareName, 0,sizeof(smartThInst -> firmwareName)); + setMacAddress(); + setModelName(); + setCameraImageName(smartThInst -> firmwareName); + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail upload URL: %s \n",__FUNCTION__, __LINE__,smtTnUploadURL); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail auth code: %s \n",__FUNCTION__, __LINE__,smtTnAuthCode); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail event quiet time: %d \n",__FUNCTION__, __LINE__,smartThInst -> event_quiet_time); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail height: %d \n",__FUNCTION__, __LINE__,sTnHeight); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): smart_thumbnail width: %d \n",__FUNCTION__, __LINE__,sTnWidth); + + //creating plugin factory instance. + pluginFactory = CreatePluginFactoryInstance(); + if (!pluginFactory) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating plugin factory instance.\n", __FUNCTION__, __LINE__); + return STH_ERROR; + } + + //create recorder + recorder = ( RdkCVideoCapturer* )pluginFactory->CreateVideoCapturer(); + if (!recorder) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error creating instance of plugin recorder.\n", __FUNCTION__, __LINE__); + return STH_ERROR; + } + + //allocate memory for yuv high resolution buffer + smartThInst -> hres_frame_info = (RDKC_PLUGIN_YUVInfo *) malloc(sizeof(RDKC_PLUGIN_YUVInfo)); + if (NULL == smartThInst -> hres_frame_info) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Frame malloc error for high res frame info. \n", __FUNCTION__ , __LINE__); + return STH_ERROR; + } + + //initialize http client + httpClient = new HttpClient(); + + /* Open the URL */ + if (NULL != httpClient) { + httpClient->open(smtTnUploadURL, dnsCacheTimeout); + } else { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to open the URL\n", __FUNCTION__, __LINE__); + } + + //Initializing thread to listen to incoming messages + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating rtMessage receive thread.\n", __FUNCTION__, __LINE__); + rtMsgInit(); + std::thread rtMessageReceiveThread(receiveRtmessage); + rtMessageReceiveThread.detach(); + + //Initializing upload thread + /* RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail upload thread.\n", __FUNCTION__, __LINE__); + std::thread uploadPayloadThread(uploadPayload); + uploadPayloadThread.detach();*/ + + return STH_SUCCESS; +} + +/** @description: Get thumbnail upload conf + * @param[in] void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::getTnUploadConf() +{ + tn_provision_info_t *stnCfg = NULL; + bool retry = true; + + // Read Thumbnail config. + stnCfg = (tn_provision_info_t*) malloc(sizeof(tn_provision_info_t)); + + if (NULL == stnCfg) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error allocating memory.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + if (STH_SUCCESS != polling_config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error initializing polling config.\n", __FILE__, __LINE__); + if (stnCfg) { + free(stnCfg); + } + return STH_ERROR; + } + + while (retry) { + if (STH_SUCCESS != readTNConfig(stnCfg)) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error reading TNConfig.\n", __FILE__, __LINE__); + } else { + break; + } + //Sleep 10 sec before retrying + sleep(10); + } + + // get url and auth + strcpy(smtTnUploadURL, stnCfg -> url); + strcpy(smtTnAuthCode, stnCfg -> auth_token); + sTnHeight = atoi(stnCfg -> height); + sTnWidth = atoi(stnCfg -> width); + + if (stnCfg) { + free(stnCfg); + stnCfg = NULL; + } + + polling_config_exit(); + + return STH_SUCCESS; +} + +/** @description: Get Event conf + * @param[in] void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::getEventConf() +{ + bool retry = true; + events_provision_info_t *eventsCfg = NULL; + + // Read event config. + eventsCfg = (events_provision_info_t*) malloc(sizeof(events_provision_info_t)); + + if (NULL == eventsCfg) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error allocating memory.\n", __FILE__, __LINE__); + return STH_ERROR; + } + + if (STH_SUCCESS != polling_config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error initializing polling config.\n", __FILE__, __LINE__); + if (eventsCfg) { + free(eventsCfg); + } + return STH_ERROR; + } + + while (retry) { + if (STH_SUCCESS != readEventConfig(eventsCfg)) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error reading EVENTS Config.\n", __FILE__, __LINE__); + } else { + break; + } + //Sleep 10 sec before retrying + sleep(10); + } + + // get event quiet interval + if (strlen(eventsCfg->quite_interval) > 0) { + smartThInst -> event_quiet_time = atoi(eventsCfg->quite_interval); + } + + if (eventsCfg) { + free(eventsCfg); + eventsCfg = NULL; + } + + polling_config_exit(); + + return STH_SUCCESS; +} + +/** @description: create the payload for smart thumbnail + * @param[in] : void + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::createPayload() +{ + //std::pair objPair; + + STH_STATUS ret = STH_ERROR; + + cv::Rect unionBox; + cv::Mat lHresRGBMat; + cv::Mat croppedObj; + //cv::Mat resizedCroppedObject; + cv::Mat resizedRGBMat; +#ifdef USE_FILE_UPLOAD + struct tm* tv = NULL; + struct timespec currTime; +#endif + { + //Acquire lock + std::unique_lock lock(smartThInst -> QMutex); + if (smartThInst -> isPayloadAvailable) + { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Payload is available.\n", __FILE__, __LINE__); + payload.tstamp = smartThInst -> ofData.currTime; + + // matrix to store color image + lHresRGBMat = cv::Mat(hres_y_height, hres_y_width, CV_8UC4); + // convert the frame to BGR format + cv::cvtColor(smartThInst -> ofData.maxBboxObjYUVFrame,lHresRGBMat, cv::COLOR_YUV2BGR_NV12); + + unionBox.x = smartThInst -> ofData.boundingBoxXOrd; + unionBox.y = smartThInst -> ofData.boundingBoxYOrd; + unionBox.width = smartThInst -> ofData.boundingBoxWidth; + unionBox.height = smartThInst -> ofData.boundingBoxHeight; + + // extracted the below logic from server scala code + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):unionBox.x %d unionBox.y %d unionBox.height %d unionBox.width %d\n", __FILE__, __LINE__, unionBox.x, unionBox.y, unionBox.height, unionBox.width); + cv::Point2f orgCenter = getActualCentroid(unionBox); + cv::Size cropSize = getCropSize(unionBox, sTnWidth, sTnHeight); + cv::Point2f allignedCenter = alignCentroid(orgCenter, lHresRGBMat, cropSize); + getRectSubPix(lHresRGBMat, cropSize, allignedCenter, croppedObj); + relativeBBox = getRelativeBoundingBox(unionBox, cropSize, allignedCenter); + +#if 0 + // create cropped object + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):before cropping object frame.\n", __FILE__, __LINE__); + croppedObj = lHresRGBMat(unionBox).clone(); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):after cropping object frame.\n", __FILE__, __LINE__); + // Resize object to fixed resolution, keep aspect ratio + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):before resizing object frame.\n", __FILE__, __LINE__); + resizeAspect(croppedObj, sTnWidth, sTnHeight, resizedCroppedObject); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):after resizing object frame.\n", __FILE__, __LINE__); + + payload.objFrame = resizedCroppedObject.clone(); + //resize the RGG data to required size. + cv::resize(lHresRGBMat,resizedRGBMat,cv::Size(sTnWidth,sTnHeight)); + payload.objFrame = resizedRGBMat.clone(); +#endif + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Resized the cropped object frame.\n", __FILE__, __LINE__); + cv::resize(croppedObj, resizedRGBMat,cv::Size(sTnWidth, sTnHeight)); + payload.objFrame = resizedRGBMat.clone(); + +#ifdef USE_FILE_UPLOAD + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + tv = gmtime(&currTime.tv_sec); + + memset(uploadFname, 0, sizeof(uploadFname)); + snprintf(uploadFname, sizeof(uploadFname), "%s/%04d%02d%02d%02d%02d%02d.jpg", STN_PATH,(tv->tm_year+1900), tv->tm_mon+1, tv->tm_mday, tv->tm_hour, tv->tm_min, tv->tm_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail upload file: %s\n",__FILE__, __LINE__, uploadFname); + //Write smart thumbnail to file. + imwrite(uploadFname, payload.objFrame); +#endif + //reset payload flag + smartThInst -> isPayloadAvailable = false; + smartThInst -> maxBboxArea = 0; + ret = STH_SUCCESS; + } else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Nothing to upload.\n", __FILE__, __LINE__); + ret = STH_NO_PAYLOAD; + } + //Release lock + lock.unlock(); + } + + return ret; +} + +/** @description: find the actual centroid of the bounding rectangle + * @param[in] bounding rectangle + * @return: central point {x,y} + */ +cv::Point2f SmartThumbnail::getActualCentroid(cv::Rect boundRect) { + + cv::Point2f pts; + + int adjustFactor = 1; // multi-by-6 to re-adjust centroid + float heightPading = 0.4; + float xPoint = adjustFactor *(boundRect.x + (boundRect.width / 2)); + float yPoint = adjustFactor *(boundRect.y + (boundRect.height /2)); + + pts.x = xPoint; + pts.y = yPoint; + + return pts; +} + +/** @description: Allign the centroid of the bounding window + * @param[in] original centroid + * @param[in] original frame resolution + * @param[in] crop window size + * @return: alligned centroid + */ +cv::Point2f SmartThumbnail::alignCentroid(cv::Point2f orgCenter, cv::Mat origFrame, cv::Size cropSize) { + + cv::Point2f pts; + + float shiftX = (orgCenter.x + (cropSize.width/2)) - origFrame.cols; + float adjustedX = orgCenter.x - STN_MAX(0, shiftX); + float shiftXleft = (adjustedX - (cropSize.width/2)); + float adjustedXfinal = adjustedX - STN_MIN(0, shiftXleft); + float shiftY = (orgCenter.y + (cropSize.height/2)) - origFrame.rows; + float adjustedY = orgCenter.y - STN_MAX(0, shiftY); + float shiftYdown = (adjustedY - (cropSize.height/2)); + float adjustedYfinal = adjustedY - STN_MIN(0, shiftYdown); + + pts.x = adjustedXfinal; + pts.y = adjustedYfinal; + + cout << "\n\n Original Center { " << orgCenter.x << ", " << orgCenter.y << " } " << "Alligned Center: { " << adjustedXfinal << ", " << adjustedYfinal << " } \n"; + cout << " Cropping Resolution {W, H}: { " << cropSize.width << ", " << cropSize.height << " } " << "Original frame Resolution {W, H}: { " << origFrame.cols << ", " << origFrame.rows << " } \n"; + cout << " Intermediate Adjustments {shiftX, adjustedX, shiftXleft}: { " << shiftX << ", " << adjustedX << ", " << shiftXleft << " } \n"; + cout << " Intermediate Adjustments {shiftY, adjustedY, shiftYdown}: { " << shiftY << ", " << adjustedY << ", " << shiftYdown << " } \n\n"; + + return pts; +} + +/** @description: find the crop size window {w, h} + * @param[in] bounding rectangle + * @param[in] minimum width of the crop window + * @param[in] minimum height of the crop window + * @return: size of the crop window + */ +cv::Size SmartThumbnail::getCropSize(cv::Rect boundRect,double w,double h) { + int newWidth = 0; + int newHeight = 0; + int adjustFactor = 1; + + cv::Size sz; + newWidth = boundRect.width; + newHeight = boundRect.height; + +#if 0 + if (boundRect.width > (boundRect.height *(w/h))) { + newWidth = boundRect.width; + } else { + newWidth = (int)(boundRect.height *(w/h)); + } + + if (boundRect.height > (boundRect.width *(h/w))) { + newHeight = boundRect.height; + } else { + newHeight = (int)(boundRect.width * (h/w)); + } +#endif + + // always ensure the minimum size of the crop size window is {w, h} + sz.height = STN_MAX(h,(newHeight* adjustFactor)); + sz.width = STN_MAX(w,(newWidth* adjustFactor)); + + return sz; +} + +/** @description: Get the bounding box ordinates related to the resolution of smart thumbnail + * @param[in] boundRect: original bounding box ordinates + * @param[in] cropSize: resolution of the cropped image + * @param[in] allignedCenter: alligned center of the bounding box + * @return: relative bounding box + */ +cv::Rect SmartThumbnail::getRelativeBoundingBox(cv::Rect boundRect, cv::Size cropSize, cv::Point2f allignedCenter) { + + cv::Rect newBBox; // to store the new bounding box co-ordinate + + // to find the relative x-ordinate and width of the bounding box in the smart thumbnail image + if (boundRect.width >= cropSize.width) { + newBBox.x = 0; + newBBox.width = cropSize.width; // restrict the width of the relative bounding box to width of the final cropped image + } + else { + float deltaX = allignedCenter.x - boundRect.x; + newBBox.x = cropSize.width/2 - deltaX; + newBBox.width = boundRect.width; + } + + // to find the relative y-ordinate and height of the bounding box in the smart thumbnail image + if (boundRect.height >= cropSize.height) { + newBBox.y = 0; + newBBox.height = cropSize.height; // restrict the height of the relative bounding box to height of the final cropped image + } + else { + float deltaY = allignedCenter.y - boundRect.y; + newBBox.y = cropSize.height/2 - deltaY; + newBBox.height = boundRect.height; + } + + return newBBox; +} + +STH_STATUS SmartThumbnail::notify( const char* status) +{ + if(!status) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Trying to use invalid memory location!! \n", __FUNCTION__ , __LINE__); + return STH_ERROR; + } + + rtMessage req; + rtMessage_Create(&req); + rtMessage_SetString(req, "status", status); + + rtError err = rtConnection_SendMessage(connectionSend, req, "RDKC.SMARTTN.STATUS"); + rtLog_Debug("SendRequest:%s", rtStrError(err)); + + if (err != RT_OK) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Error sending msg via rtmessage\n", __FILE__,__LINE__); + } + rtMessage_Release(req); + + return STH_SUCCESS; +} + +/** @description: destroy the instance of smart thumbnail + * @param[in] : void + * @return : STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::destroy() +{ + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) destroy smart thumbnail!!!\n", __FUNCTION__ , __LINE__ ); + + //Exit the rtmessage receive and upload thread. + rtmessageSTHThreadExit = true; + //uploadSTHThreadExit = true; + + if (smartThInst -> hres_frame_info) { + free(smartThInst -> hres_frame_info); + smartThInst -> hres_frame_info = NULL; + } + + //Delete the smart thumbnail instance + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Deleting the smart thumbnail instance!!!\n", __FUNCTION__ , __LINE__); + if (smartThInst) { + delete smartThInst; + smartThInst =NULL; + } + +#ifdef LEGACY_CFG_MGR + config_release(); +#endif + + return STH_SUCCESS; +} + +/** @description: Callback function for dynamic logging + * @param[in] hdr : pointer to rtMessage Header + * @param[in] buff : buffer for data received via rt message + * @param[in] n : number of bytes received + * @return: void + */ +void SmartThumbnail::dynLogOnMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + char const* module = NULL; + char const* logLevel = NULL; + + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + //Handle the rtmessage request + if (rtMessageHeader_IsRequest(hdr)) { + char* tmp_buff = NULL; + uint32_t tmp_buff_length = 0; + + rtMessage_ToString(req, &tmp_buff, &tmp_buff_length); + rtLog_Info("Req : %.*s", tmp_buff_length, tmp_buff); + free(tmp_buff); + + rtMessage_GetString(req, "module", &module); + rtMessage_GetString(req, "logLevel", &logLevel); + + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.DYNAMICLOG","(%s):%d Module name: %s\n", __FUNCTION__, __LINE__, module); + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.DYNAMICLOG","(%s):%d log level: %s\n", __FUNCTION__, __LINE__, logLevel); + + RDK_LOG_ControlCB(module, NULL, logLevel, 1); + + // create response + rtMessage res; + rtMessage_Create(&res); + rtMessage_SetString(res, "reply", "Success"); + rtConnection_SendResponse(con, hdr, res, 1000); + rtMessage_Release(res); + } + rtMessage_Release(req); +} + +/** @description : Callback function to capture high resolution frame + * @param[in] hdr : constant pointer rtMessageHeader + * @param[in] buff : constant pointer uint8_t + * @param[in] n : uint32_t + * @param[in] closure : void pointer + * @return: void + */ +void SmartThumbnail::onMsgCaptureFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + int processPID = -1; + int ret = STH_ERROR; + char const* strFramePTS = NULL; + uint64_t lResFramePTS = 0; + uint64_t hResFramePTS = 0; + struct timespec currTime; + + //clock the current time + memset (&currTime, 0, sizeof(struct timespec)); + clock_gettime(CLOCK_REALTIME, &currTime); + + // read the message received + (void) closure; + rtMessage m; + rtMessage_FromBytes(&m, buff, n); + rtMessage_GetInt32(m, "processID", &processPID); + rtMessage_GetString(m, "timestamp", &strFramePTS); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) strFramePTS:%s \n", __FUNCTION__ , __LINE__, strFramePTS); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) capture invoked by process %d \n", __FUNCTION__ , __LINE__, processPID); + + std::istringstream iss(strFramePTS); + iss >> lResFramePTS; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): lResframePTS:%llu\n", __FUNCTION__, __LINE__, lResFramePTS); + + rtMessage_Release(m); + + + if (NULL == smartThInst -> hres_frame_info) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):High Res YUV Frame malloc error \n", __FUNCTION__ , __LINE__); + } + memset(smartThInst -> hres_frame_info, 0, sizeof(RDKC_PLUGIN_YUVInfo)); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):prev_time :%d\n", __FUNCTION__ , __LINE__, smartThInst -> prev_time); + + // ignore frames and metadata for event_quiet_time + if( (currTime.tv_sec < (smartThInst -> prev_time + smartThInst -> event_quiet_time)) && (0 != smartThInst -> prev_time) ) { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Within event quiet time, Ignoring event, time passed: %lu\n", __FUNCTION__ , __LINE__, (currTime.tv_sec- smartThInst -> prev_time)); + return; + } + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Not within event quiet time, Capturing frame.\n", __FUNCTION__ , __LINE__); + + //read the 720*1280 YUV data. + ret = smartThInst -> recorder -> ReadYUVData(STN_HRES_BUFFER_ID, smartThInst -> hres_frame_info); + if( STH_SUCCESS != ret) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Error Reading High Res YUV Frame \n", __FUNCTION__, __LINE__); + } + + hResFramePTS = smartThInst -> hres_frame_info -> mono_pts; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): hResframePTS:%llu\n", __FUNCTION__, __LINE__, hResFramePTS); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Time gap (hResFramePTS - lResframePTS):%llu\n", __FUNCTION__, __LINE__, (hResFramePTS - lResFramePTS)); + +#if 0 + if(!smartThInst -> hres_yuvDataMemoryAllocationDone) { + smartThInst -> hres_y_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + smartThInst -> hres_uv_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + smartThInst -> hres_yuvData = (unsigned char *) malloc((smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char)); + + smartThInst -> hres_yuvDataMemoryAllocationDone = true; + } + + //Acquire lock + std::unique_lock lock(smartThInst -> QMutex); + memset( smartThInst -> hres_yuvData, 0, (smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char) ); + memcpy( smartThInst -> hres_yuvData, smartThInst -> hres_frame_info->y_addr, smartThInst -> hres_y_size); + memcpy( smartThInst -> hres_yuvData + smartThInst -> hres_y_size, smartThInst -> hres_frame_info->uv_addr, smartThInst -> hres_uv_size); + //Release lock + lock.unlock(); +#endif + + //Ensure metadata is received after the frame is captured. + smartThInst -> isHresFrameReady = true; +} + +/** @description : Callback function to generate smart thumbnail based on motion. + * @param[in] hdr : constant pointer rtMessageHeader + * @param[in] buff : constant pointer uint8_t + * @param[in] n : uint32_t + * @param[in] closure : void pointer + * @return: void + */ +void SmartThumbnail::onMsgProcessFrame(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + double motionScore = 0.0; + int32_t eventType = 0; + int32_t boundingBoxXOrd = 0; + int32_t boundingBoxYOrd = 0; + int32_t boundingBoxHeight = 0; + int32_t boundingBoxWidth = 0; + uint64_t curr_time = 0; + char const* s_curr_time = NULL; + char const* strFramePTS = NULL; + uint64_t lResFramePTS = 0; + + cv::Mat l_hres_yuvMat; + cv::Mat l_hres_RGBMat; + cv::Mat cropped_object; + cv::Mat resized_cropped_object; + int lBboxArea = 0; + + (void) closure; + + rtMessage m; + rtMessage_FromBytes(&m, buff, n); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) process frame invoked.\n", __FUNCTION__ , __LINE__); + + //read the metadata. + rtMessage_GetString(m, "timestamp", &strFramePTS); + rtMessage_GetInt32(m, "event_type", &eventType); + rtMessage_GetDouble(m, "motionScore", &motionScore); + rtMessage_GetInt32(m, "boundingBoxXOrd", &boundingBoxXOrd); + rtMessage_GetInt32(m, "boundingBoxYOrd", &boundingBoxYOrd); + rtMessage_GetInt32(m, "boundingBoxHeight", &boundingBoxHeight); + rtMessage_GetInt32(m, "boundingBoxWidth", &boundingBoxWidth); + rtMessage_GetString(m, "currentTime", &s_curr_time); + + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): strFramePTS:%s \n", __FUNCTION__ , __LINE__, strFramePTS); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): event Type: %d\n",__FILE__, __LINE__, eventType); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): motionScore: %f\n",__FILE__, __LINE__, motionScore); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxXOrd:%d\n", __FILE__, __LINE__,boundingBoxXOrd); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxYOrd:%d\n", __FILE__, __LINE__,boundingBoxYOrd); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxHeight:%d\n", __FILE__, __LINE__,boundingBoxHeight); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): boundingBoxWidth:%d\n", __FILE__, __LINE__,boundingBoxWidth); + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): curr timestamp:%s\n", __FILE__, __LINE__,s_curr_time); + + std::istringstream iss(strFramePTS); + iss >> lResFramePTS; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): lResframePTS:%llu\n", __FUNCTION__, __LINE__, lResFramePTS); + iss.clear(); + + iss.str(s_curr_time); + iss >> curr_time; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): curr timestamp (uint64):%llu\n", __FILE__, __LINE__,curr_time); + iss.clear(); + + lBboxArea = boundingBoxWidth * boundingBoxHeight; + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Current Area : %d\n", __FILE__ , __LINE__, lBboxArea); + + // if motion is detected update the metadata. + if ((smartThInst -> isHresFrameReady) && + //(motionScore != 0.0) && + (eventType == 4) && + (lBboxArea > smartThInst -> maxBboxArea)) { + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Processing metadata .\n", __FUNCTION__ , __LINE__); + + smartThInst -> isHresFrameReady = false; + //Update the max bounding area. + smartThInst -> maxBboxArea = lBboxArea; + { + //Acquire lock + std::unique_lock lock(smartThInst -> QMutex); + if (!smartThInst -> isPayloadAvailable) { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Payload will be available for this interval.\n", __FUNCTION__ , __LINE__); + //To indicate payload will there to upload + smartThInst -> isPayloadAvailable = true; + } + updateObjFrameData(boundingBoxXOrd, boundingBoxYOrd, boundingBoxWidth, boundingBoxHeight, curr_time); + lock.unlock(); + } + + smartThInst -> prev_time = curr_time; + } else { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.SMARTTHUMBNAIL","%s(%d) Metadata discarded .\n", __FUNCTION__ , __LINE__); + } + + rtMessage_Release(m); +} + +/** @description : resizes smart thumbnail to default height & width + * @param[in] im : cropped image + * @param[in] h : height + * @param[in] w : width + * @param[out] im_resized : resized smart thumbnail + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ + +#if 0 +STH_STATUS SmartThumbnail::resizeAspect(cv::Mat im, int w, int h, cv::Mat& im_resized) +{ + im_resized = cv::Mat(); + + if (im.empty()) { + //std::cerr << __FUNCTION__ << " : input image is empty" << std::endl; + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):input image is empty.\n", __FUNCTION__ , __LINE__); + return STH_ERROR; + } + + int new_w = im.cols; + int new_h = im.rows; + + if (((float)w/new_w) < ((float)h/new_h)) { + new_w = w; + new_h = (im.rows * w)/im.cols; + } else { + new_h = h; + new_w = (im.cols * h)/im.rows; + } + + cv::Mat resized; + cv::resize(im,resized,cv::Size(new_w, new_h)); + + // create image the size of the net layer + im_resized = cv::Mat(h,w,resized.type()); + + // embed image into im_resized image + int dx = (w-new_w)/2; + int dy = (h-new_h)/2; + + resized.copyTo(im_resized(cv::Rect(dx,dy,resized.cols, resized.rows))); + + return STH_SUCCESS; +} +#endif + +/** @description : resizes smart thumbnail to default height & width + * @param[in] : x ordinate of bounding box + * @param[in] : y ordinate of bounding box + * @param[in] : width of bounding box + * @param[in] : height of bounding box + * @return : void + */ +void SmartThumbnail::updateObjFrameData(int32_t boundingBoxXOrd,int32_t boundingBoxYOrd,int32_t boundingBoxWidth,int32_t boundingBoxHeight, uint64_t currTime) +{ + unsigned char* hres_yuvData = NULL; +#if 0 + char tmpFname[256] = {'\0'}; + cv::Mat lHresRGBMat; +#endif + smartThInst -> ofData.boundingBoxXOrd = boundingBoxXOrd; + smartThInst -> ofData.boundingBoxYOrd = boundingBoxYOrd; + smartThInst -> ofData.boundingBoxWidth = boundingBoxWidth; + smartThInst -> ofData.boundingBoxHeight = boundingBoxHeight; + smartThInst -> ofData.currTime = currTime; + +#if 0 + snprintf(tmpFname, sizeof(tmpFname), "%llu.jpg", smartThInst -> ofData.currTime); +#endif + + smartThInst -> hres_y_height = smartThInst -> hres_frame_info->height; + smartThInst -> hres_y_width = smartThInst -> hres_frame_info->width; + smartThInst -> hres_y_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + smartThInst -> hres_uv_size = smartThInst -> hres_frame_info->width * smartThInst -> hres_frame_info->height; + hres_yuvData = (unsigned char *) malloc((smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char)); + + //Acquire lock + //std::unique_lock lock(stnMutex); + memset( hres_yuvData, 0, (smartThInst -> hres_y_size + smartThInst -> hres_uv_size) * sizeof(unsigned char) ); + memcpy( hres_yuvData, smartThInst -> hres_frame_info->y_addr, smartThInst -> hres_y_size); + memcpy( hres_yuvData + smartThInst -> hres_y_size, smartThInst -> hres_frame_info->uv_addr, smartThInst -> hres_uv_size); + //Release lock + //lock.unlock(); + + //Full 720*1280 frame containing max bounding box + smartThInst -> ofData.maxBboxObjYUVFrame = cv::Mat(smartThInst -> hres_frame_info -> height + (smartThInst -> hres_frame_info -> height)/2, smartThInst -> hres_frame_info -> width, CV_8UC1, hres_yuvData).clone(); + + if(hres_yuvData) { + free(hres_yuvData); + hres_yuvData = NULL; + } + +#if 0 + cv::cvtColor(smartThInst -> ofData.maxBboxObjYUVFrame,lHresRGBMat, cv::COLOR_YUV2BGR_NV12); + cv::rectangle(lHresRGBMat, cv::Rect(smartThInst -> ofData.boundingBoxXOrd, smartThInst -> ofData.boundingBoxYOrd, smartThInst -> ofData.boundingBoxWidth, smartThInst -> ofData.boundingBoxHeight), cv::Scalar(0,0,255), 2); + + cv::putText(lHresRGBMat, (std::to_string(smartThInst -> ofData.currTime)).c_str(), cv::Point(20, 20), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + cv::putText(lHresRGBMat, (std::to_string(smartThInst -> ofData.boundingBoxHeight)).c_str(), cv::Point(40, 40), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + cv::putText(lHresRGBMat, (std::to_string(smartThInst -> ofData.boundingBoxWidth)).c_str(), cv::Point(60, 60), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + cv::putText(lHresRGBMat, (std::to_string(smartThInst -> ofData.boundingBoxWidth*boundingBoxHeight)).c_str(), cv::Point(80, 80), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); + + imwrite(tmpFname, lHresRGBMat); +#endif +} + +/** @description: register callback for receiving msg via rtmessage. + * @param[in] void. + * @return: STH_SUCCESS on success, STH_ERROR otherwise + */ +STH_STATUS SmartThumbnail::rtMsgInit() +{ + rtLog_SetLevel(RT_LOG_INFO); + rtLog_SetOption(rdkLog); + + rtConnection_Create(&connectionSend, "SMART_TN_SEND", "tcp://127.0.0.1:10001"); + rtConnection_Create(&smartThInst -> connectionRecv, "SMART_TN_RECV", "tcp://127.0.0.1:10001"); + + rtConnection_AddListener(smartThInst -> connectionRecv, "RDKC.SMARTTN.CAPTURE",onMsgCaptureFrame, NULL); + rtConnection_AddListener(smartThInst -> connectionRecv, "RDKC.SMARTTN.METADATA",onMsgProcessFrame, NULL); + + //Add listener for dynamic log topics + rtConnection_AddListener(smartThInst -> connectionRecv, RTMSG_DYNAMIC_LOG_REQ_RES_TOPIC, dynLogOnMessage, smartThInst -> connectionRecv); + + return STH_SUCCESS; +} + +/** @description: thread routine to upload smart thumbnail payload + * @param[in] : time left + * @return: void + */ +void SmartThumbnail::uploadPayload(time_t timeLeft) +{ + int curlCode = 0; + long response_code = 0; +#ifdef USE_FILE_UPLOAD + char *data = NULL; + struct stat fileStat; + int fileLen = 0; + int readLen = 0; + char *ptr = NULL; + char readBuf[STN_UPLOAD_SEND_LEN]; + int fd = 0; + //char uploadFname[256] = {0}; + //struct tm* tv = NULL; +#else + char *dataPtr = NULL; + int dataLen = 0; + //std::vector dataPtr; + //dataPtr.reserve(STN_DATA_MAX_SIZE); +#endif + char packHead[STN_UPLOAD_SEND_LEN+1]; + int retry = 0; + + struct timespec currTime; + struct timespec startTime; + memset(&startTime, 0, sizeof(startTime)); + memset(&currTime, 0, sizeof(currTime)); + + //clock the start time + clock_gettime(CLOCK_REALTIME, &startTime); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):Uploading smart thumbnail!!! Time left %ld\n", __FILE__, __LINE__, timeLeft); + + while (true) { + //clock the current time + memset(&currTime, 0, sizeof(currTime)); + clock_gettime(CLOCK_REALTIME, &currTime); + + //Check for max retry or time limit and break if so + if ( (retry >= STN_MAX_RETRY_COUNT) || + ((currTime.tv_sec - startTime.tv_sec) >= timeLeft) ) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL", "%s(%d): Max retry count/time exceeded, Retry count %d Time spent %d. currTime.tv_sec %d startTime.tv_sec %d Upload failed!!!\n", __FILE__,__LINE__, retry, (currTime.tv_sec - startTime.tv_sec), currTime.tv_sec, startTime.tv_sec); + break; + } + + if (0 == retry) { + //check for empty payload and break if so + if (payload.objFrame.empty()) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d):payload is empty!!!\n", __FILE__, __LINE__); + break; + } +#if 0 + tv = gmtime(&currTime.tv_sec); + snprintf(uploadFname, sizeof(uploadFname), "%s/%04d%02d%02d%02d%02d%02d.jpg", STN_PATH,(tv->tm_year+1900), tv->tm_mon+1, tv->tm_mday, tv->tm_hour, tv->tm_min, tv->tm_sec); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Creating smart thumbnail upload file: %s\n",__FILE__, __LINE__, uploadFname); + //Write smart thumbnail to file. + imwrite(uploadFname, payload.objFrame); +#endif + +#ifdef USE_FILE_UPLOAD + /* get file attribute */ + if (stat(uploadFname, &fileStat) < 0) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): invalid file [%s], errmsg=%s!!!\n",__FILE__, __LINE__, uploadFname, strerror(errno)); + break; + /*retry++; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retry again for %d times!!!\n",__FILE__,__LINE__,retry); + continue;*/ + } + + fileLen = fileStat.st_size; + RDK_LOG(RDK_LOG_INFO, "LOG.RDK.SMARTTHUMBNAIL", "%s(%d):Length of the smart thumbnail file to be uploaded: %d!!!\n", __FILE__, __LINE__,fileLen); + + fd = open(uploadFname, O_RDONLY); + if (fd <= 0) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to Open smart thumbnail upload File :%s !!!\n", __FILE__, __LINE__, uploadFname); + break; + /*retry++; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retrying again for %d times!!!\n",__FILE__,__LINE__,retry); + continue;*/ + } + + data =(char*)malloc(fileLen*sizeof(char)); + if (NULL == data) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Failed to allocate memory :%s !!!\n", __FILE__, __LINE__, uploadFname); + close(fd); + break; + /*retry++; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retrying again for %d times!!!\n",__FUNCTION__,__LINE__,retry); + continue;*/ + } + + memset(data,0,fileLen); + memset(readBuf,0,STN_UPLOAD_SEND_LEN); + ptr=data; + + // read thumbnail file into buffer + while((readLen = read(fd, readBuf, sizeof(readBuf))) > 0) { + memcpy(ptr, readBuf, readLen); + ptr += readLen; + memset(readBuf,0,STN_UPLOAD_SEND_LEN); + } +#else + dataLen = payload.objFrame.total() * payload.objFrame.elemSize(); + dataPtr = reinterpret_cast(payload.objFrame.data); + //cv::imencode(".jpg", payload.objFrame, dataPtr); + //dataLen = dataPtr.size(); + +#endif + } + + /* Add Header */ + httpClient->resetHeaderList(); + httpClient->addHeader( "Expect", ""); //removing expect header condition by explicitly setting Expect header to "" + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%s", smtTnAuthCode); + httpClient->addHeader( "Authorization", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "image/jpeg"); + httpClient->addHeader( "Content-Type", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "Sercomm %s %s %s", modelName, firmwareName, macAddress); + httpClient->addHeader( "User-Agent", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%llu", payload.tstamp); + httpClient->addHeader( "X-EVENT-TIME", packHead); + memset(packHead, 0, sizeof(packHead)); + //SMTN's Bounding box of Motion Detection area + snprintf(packHead, sizeof(packHead), "%d, %d, %d, %d", relativeBBox.x, relativeBBox.y, relativeBBox.width, relativeBBox.height); + smartThInst->httpClient->addHeader( "X-BoundingBox", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "OFF"); + smartThInst->httpClient->addHeader("X-VIDEO-RECORDING", packHead); + +#ifdef USE_FILE_UPLOAD + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%s", uploadFname); + httpClient->addHeader( "X-FILE-NAME", packHead); + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%d",fileLen); + httpClient->addHeader( "Content-Length", packHead); + + //upload data + curlCode = httpClient->post_binary(smtTnUploadURL, data, &response_code, fileLen); +#else + memset(packHead, 0, sizeof(packHead)); + snprintf(packHead, sizeof(packHead), "%d",dataLen); + httpClient->addHeader( "Content-Length", packHead); + + //curlCode = httpClient->post_binary(smtTnUploadURL, reinterpret_cast (&dataPtr[0]), &response_code, dataLen); + curlCode = httpClient->post_binary(smtTnUploadURL, dataPtr, &response_code, dataLen); +#endif + + if ((response_code >= RDKC_HTTP_RESPONSE_OK) && (response_code < RDKC_HTTP_RESPONSE_REDIRECT_START)) { + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart Thumbnail uploaded successfully with header X-EVENT-TIME: %llu X-BoundingBox: %d %d %d %d X-VIDEO-RECORDING :OFF\n", __FUNCTION__, __LINE__, payload.tstamp, relativeBBox.x, relativeBBox.y, relativeBBox.width, relativeBBox.height); + +/*#ifdef USE_FILE_UPLOAD + if(NULL != data) { + free(data); + data = NULL; + } + close(fd); +#endif*/ + break; + + } else { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Smart Thumbnail upload failed, with response code:%lu!!!\n",__FUNCTION__,__LINE__, response_code); + retry++; + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Retrying again for %d times!!!\n",__FUNCTION__,__LINE__,retry); + } + +/*#ifdef USE_FILE_UPLOAD + if(NULL != data) { + free(data); + data = NULL; + } + close(fd); +#endif*/ + } + +#ifdef USE_FILE_UPLOAD + if(NULL != data) { + free(data); + data = NULL; + } + close(fd); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Removing smart thumbnail upload file: %s\n",__FILE__, __LINE__, uploadFname); + unlink (uploadFname); +#endif +} + + +/** @description : thread routine to listen to messages + * @param[in] : void. + * @return : void + */ +void SmartThumbnail::receiveRtmessage() +{ + while (!smartThInst -> rtmessageSTHThreadExit) { + smartThInst -> err = rtConnection_Dispatch(smartThInst -> connectionRecv); + if (smartThInst -> err != RT_OK) { + //rtLog_Debug("dispatch:%s", rtStrError(err)); + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): dispatch:%s",__FUNCTION__,__LINE__,rtStrError(smartThInst -> err)); + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.SMARTTHUMBNAIL","%s(%d): Error receiving msg via rtmessage\n",__FUNCTION__,__LINE__); + } + usleep(10000); + } +} + +/** + * @description: This function is used to get the camera firmware version. + * + * @param[out]: void + * + * @return: Error code. + */ +int SmartThumbnail::setCameraImageName(char *out) +{ + size_t max_line_length = FW_NAME_MAX_LENGTH; + char *file_buffer; + char *locate_1 = NULL; + FILE* fp; + char* temp = out; + + fp = fopen("/version.txt","r"); + if (NULL == fp) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.CVRUPLOAD","%s(%d): Error in opening version.txt \n", __FILE__, __LINE__); + return RDKC_FAILURE; + } + + file_buffer = (char*)malloc(FW_NAME_MAX_LENGTH + 1); + if(file_buffer == NULL) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.CVRUPLOAD","%s(%d): Error in malloc \n", __FILE__, __LINE__); + fclose(fp); + return RDKC_FAILURE; + } + + while(getline(&file_buffer,&max_line_length,fp) != -1) + { + /* find the imagename string */ + locate_1 = strstr(file_buffer,"imagename"); + if(locate_1) + { + locate_1 += strlen("imagename:"); + /* copy the contents till linefeed */ + while(*locate_1 != '\n') + *out++ = *locate_1++; + free(file_buffer); + file_buffer = NULL; + fclose(fp); + return RDKC_SUCCESS; + } + } + + /* unable to get the image name */ + strcpy(out,"imagename entry not found"); + free(file_buffer); + file_buffer = NULL; + fclose(fp); + return RDKC_SUCCESS; +} + +/** + * @description: This function is used to set the camera firmware version. + * @param[out]: void + * @return: Error code. +* */ +int SmartThumbnail::setModelName() +{ +#ifdef USE_MFRLIB + mfrSerializedData_t stdata = {NULL, 0, NULL}; + mfrSerializedType_t stdatatype = mfrSERIALIZED_TYPE_MODELNAME; + char mac[CONFIG_STRING_MAX]; + + if (mfrGetSerializedData(stdatatype, &stdata) == mfrERR_NONE) { + strncpy(modelName,stdata.buf,stdata.bufLen); + modelName[stdata.bufLen] = '\0'; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.CVRPOLL","%s(%d):Model Name = %s,%s,%d\n",__FILE__, __LINE__,modelName,stdata.buf,stdata.bufLen); + if (stdata.freeBuf != NULL) { + stdata.freeBuf(stdata.buf); + stdata.buf = NULL; + } + } + else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.CVRPOLL","%s(%d):GET ModelName failed : %d\n", __FILE__, __LINE__); + } +#endif +} + +/** + * @description: This function is used to set the camera's mac address. + * @param[out]: void + * @return: Error code. +* */ +int SmartThumbnail::setMacAddress() +{ + char mac[CONFIG_STRING_MAX] = {0}; + +#ifdef USE_MFRLIB + mfrSerializedData_t stdata = {NULL, 0, NULL}; + mfrSerializedType_t stdatatype = mfrSERIALIZED_TYPE_DEVICEMAC; + + if (mfrGetSerializedData(stdatatype, &stdata) == mfrERR_NONE) { + strncpy(mac,stdata.buf,stdata.bufLen); + mac[stdata.bufLen] = '\0'; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.CVRPOLL","%s(%d):mac= %s,%s,%d\n",__FILE__, __LINE__,mac,stdata.buf,stdata.bufLen); + + char tmpMac[CONFIG_STRING_MAX+1] = {0}; + char *tmpField; + int fieldNum = 0; + + strcpy(tmpMac, mac); + tmpField = strtok(tmpMac, ":"); + + while (tmpField != NULL && fieldNum < 6) { + char *chk; + unsigned long tmpVal; + + tmpVal = strtoul(tmpField, &chk, 16); + + if (tmpVal > 0xff) { + RDK_LOG( RDK_LOG_WARN,"LOG.RDK.CVRPOLL","field %d value %0x out of range\n", fieldNum, tmpVal); + } + if (*chk != 0) { + RDK_LOG( RDK_LOG_WARN,"LOG.RDK.CVRPOLL","Non-digit character %c (%0x) detected in field %d\n", *chk, *chk, fieldNum); + + } + fieldNum++; + strcat(macAddress, tmpField); + tmpField = strtok(NULL, ":"); + } + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.CVRPOLL","%s(%d):mac address= %s\n",__FILE__, __LINE__,macAddress); + if (stdata.freeBuf != NULL) { + stdata.freeBuf(stdata.buf); + stdata.buf = NULL; + } + + } + else { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.CVRPOLL","%s(%d):GET MAC failed : %d\n", __FILE__, __LINE__); + } +#endif +} diff --git a/src/Makefile b/src/Makefile new file mode 100755 index 0000000..1b51368 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,60 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## + +# Add dependent libraries +USE_HTTPCLIENT = yes +USE_RFCCONFIG = yes +USE_CONFIGMGR = yes +USE_RTMESSAGE = yes +SUPPORT_MXML = yes + +include ${RDK_PROJECT_ROOT_PATH}/utility/AppsRule.mak + +CFLAGS += -DSUPPORT_IMAGETOOLS +ifeq ($(XCAM_MODEL), SCHC2) +CFLAGS += -DUSE_MFRLIB +CFLAGS += -DXCAM2 +endif + +SRCS += thumbnailUpload.cpp +SRCS += thumbnail_upload_main.cpp + +OBJS = $(SRCS:.cpp=.o) + +BIN = thumbnail_upload + +##all: $(BIN) install +all: $(BIN) +RM = rm +INSTALL = install + +$(OBJS): %.o: %.cpp + $(CXX) -c $(CFLAGS) $< -o $@ + +$(BIN): $(OBJS) + $(CXX) $(CFLAGS) -o $@ $(OBJS) $(LIBFLAGS) + +clean: + $(RM) -rf $(OBJS) *~ $(BIN) + +install: + $(INSTALL) -D $(BIN) ${RDK_SDROOT}/usr/local/bin/$(BIN) + +uninstall: + $(RM) -f $(RDK_SDROOT)/usr/local/bin/$(BIN) diff --git a/src/thumbnailUpload.cpp b/src/thumbnailUpload.cpp new file mode 100644 index 0000000..6fb820b --- /dev/null +++ b/src/thumbnailUpload.cpp @@ -0,0 +1,1197 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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 "thumbnailUpload.h" + +ThumbnailUpload *ThumbnailUpload::thumbnailUpload = NULL; +int ThumbnailUpload::tn_upload_interval = DEFAULT_THUMBNAIL_UPLOAD_PASSIVE_INTERVAL; +bool ThumbnailUpload::tn_upload_enable = DEFAULT_THUMBNAIL_UPLOAD_ENABLE; +volatile sig_atomic_t ThumbnailUpload::term_flag = 0; +int ThumbnailUpload::activeUploadDuration = 0; + +bool ThumbnailUpload::isActiveInterval = false; +char ThumbnailUpload::fw_name[FW_NAME_MAX_LENGTH] = ""; +char ThumbnailUpload::ver_num[VER_NUM_MAX_LENGTH] = ""; +char ThumbnailUpload::mac_string[THUMBNAIL_UPLOAD_MAC_STRING_LEN+1] = ""; +unsigned int ThumbnailUpload::activeModeUploadCounter = 0; +int ThumbnailUpload::uploadRetryCount = 0; + +rtConnection ThumbnailUpload::con = NULL; + +/** @description initialize rtmessage connection. + */ +void ThumbnailUpload::rtConnection_init() +{ + rtLog_SetLevel(RT_LOG_INFO); + rtLog_SetOption(rdkLog); + rtConnection_Create(&con, "THUMBNAILUPLOAD", WEBPA_ADDRESS); + //Add listener for thumbnail topics + rtConnection_AddListener(con, RTMSG_THUMBNAIL_TOPIC, onMessage, con); + //Add listener for dynamic log topics + rtConnection_AddListener(con, RTMSG_DYNAMIC_LOG_REQ_RES_TOPIC, dynLogOnMessage, con); +} + +/** @description ThumbnailUpload constructor. + */ +ThumbnailUpload::ThumbnailUpload():http_client(NULL) + , tn_upload_file_name(NULL) + , m_avgUploadSpeed(0) + , m_count(0) + , json_prov_tn_upload_enabled(-1) + , liveCacheConf(NULL) +{ + char url_string[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH+1]; + m_smVector.reserve(10); + http_client = new HttpClient(); + if(NULL == http_client) + { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to create HttpClient instance \n", __FILE__, __LINE__); + } + + /*get upload url*/ + strncpy(tn_upload_server_url, DEFAULT_THUMBNAIL_UPLOAD_URL, THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH); + strncpy(tn_upload_auth_token, DEFAULT_THUMBNAIL_UPLOAD_AUTH_TOKEN, THUMBNAIL_UPLOAD_AUTH_MAX_LENGTH); + + /* get upload file name */ + tn_upload_file_name = (char*)malloc(SIZE); + if(NULL != tn_upload_file_name) + { +#ifdef SUPPORT_IMAGETOOLS + sprintf(tn_upload_file_name,"%s",SNAPSHOT_FILE); +#else + snprintf(tn_upload_file_name, strlen(gcpThumbnailSnapshotPath)+1, "%s", gcpThumbnailSnapshotPath); +#endif + } + + memset(url_string, 0, sizeof(url_string)); + snprintf(url_string, THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH, "%s/%s/thumbnail", tn_upload_server_url, mac_string); + + /* Open the URL */ + if(NULL != http_client) + { + /* Create connection */ + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Opening http client connection with URL %s\n", __FILE__, __LINE__,url_string); + http_client->open((char*)url_string, DEFAULT_DNS_CACHE_TIMEOUT); + } + else + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to open the URL\n", __FILE__, __LINE__); + } + + memset(cmd,0,MAXSIZE); + snprintf(cmd,sizeof(cmd)-1, "rdkc_snapshooter %s %d %d %d", SNAPSHOT_FILE, COMPRESSION_SCALE, TN_OP_WIDTH, TN_OP_HEIGHT); + + liveCacheConf = (livecache_provision_info_t*) malloc(sizeof(livecache_provision_info_t)); + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): ThumbnailUpload constructor \n", __FILE__, __LINE__); + +} + +void ThumbnailUpload::rtConnection_destroy() +{ + rtConnection_Destroy(con); +} + +/** @description ThumbnailUpload destructor. + */ +ThumbnailUpload::~ThumbnailUpload() +{ + if(NULL != http_client) + { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Closing http client connection.\n", __FILE__, __LINE__); + http_client->close(); + } + + if(NULL != http_client) + { + delete http_client; + http_client = NULL; + } + if(NULL != tn_upload_file_name) + { + free(tn_upload_file_name); + tn_upload_file_name = NULL; + } + + if(liveCacheConf) { + free(liveCacheConf); + liveCacheConf = NULL; + } + + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): ThumbnailUpload destructor \n", __FILE__, __LINE__); +} + +/** + * @description Check file lock used for process control. + * + * @param fname, file name to be checked. + * + * @return fd, file descriptor. + * + */ +int ThumbnailUpload::checkTNUploadfilelock(char *fname) +{ + int fd = -1; + pid_t pid = -1; + + char str[50] = "thumbnail_upload" ; + + fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0644); + if(fd < 0 && errno == EEXIST) + { + fd = open(fname, O_RDONLY, 0644); + if (fd >= 0) + { + read(fd, &pid, sizeof(pid)); + kill(pid, SIGTERM); + close(fd); + sleep(1); + if (CheckAppsPidAlive( (char*)str , pid)) + { + kill(pid, SIGTERM); + } + } + unlink(fname); + return -2; + } + + return fd; +} + +/** + * @description: This function is used to control Thumbnail upload process. + * + * @param: process- main process. + * + * @return: Error code. + */ +int ThumbnailUpload:: controlTNUploadProcess(char *process) +{ + pid_t pid = 0; + int file_fd = 0; + + file_fd = checkTNUploadfilelock((char*)LOCK_FILENAME_TNU); + if ( -2 == file_fd) + { + file_fd = checkTNUploadfilelock((char*)LOCK_FILENAME_TNU); + } + + if (file_fd < 0) + { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): %s run error!\n", __FILE__, __LINE__, process); + unlink(LOCK_FILENAME_TNU); + TNUExit(); + return RDKC_FAILURE; + } + + pid = getpid(); + write(file_fd, &pid, sizeof(pid)); + close(file_fd); + + return RDKC_SUCCESS; + + +} + +/** @description: This function is to get thumbnail upload attribute via JSON prov + * + * @param: void. + * + * @return: Error code, success or failure. + * + */ +bool ThumbnailUpload::getTNUploadProvAttr() +{ + bool ret = true; + + if(!liveCacheConf) + { + ret = false; + return ret; + } + + if(RDKC_SUCCESS != polling_config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): polling config init failure.\n", __FILE__, __LINE__); + ret = false; + } else { + /* Read live cache config */ + memset(liveCacheConf, 0, sizeof(livecache_provision_info_t)); + if (RDKC_SUCCESS != readLiveCacheConfig(liveCacheConf)) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Error reading livecache.conf.\n", __FILE__, __LINE__); + ret = false; + } else { + json_prov_tn_upload_enabled = atoi(liveCacheConf->enable); + /* Check thumbnail upload is enabled */ + if(json_prov_tn_upload_enabled) { + memset(tn_upload_server_url, 0, sizeof(tn_upload_server_url)); + memset(tn_upload_auth_token, 0, sizeof(tn_upload_auth_token)); + + strncpy(tn_upload_server_url, liveCacheConf->url, strlen(liveCacheConf->url)); + tn_upload_server_url[strlen(liveCacheConf->url)] = '\0'; + strncpy(tn_upload_auth_token, liveCacheConf->auth_token, strlen(liveCacheConf->auth_token)); + tn_upload_auth_token[strlen(liveCacheConf->auth_token)] = '\0'; + } else { + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): thumbnail upload not enabled.\n", __FILE__, __LINE__); + ret = false; + } + } + } + polling_config_exit(); + + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Thumbnail Configuration: URL %s, enable %d\n", __FILE__, __LINE__, tn_upload_server_url, json_prov_tn_upload_enabled); + + return ret; +} + + +/** + * @description: This function is used to get the instance for Thumbnail upload. + * + * @param: void + * + * @return: Thumbnail upload instance. + */ +ThumbnailUpload *ThumbnailUpload::getTNUploadInstance() +{ + + if (NULL == thumbnailUpload) + { + thumbnailUpload = new ThumbnailUpload(); + } + + int ret = getCameraImageName(fw_name); + if (ret == RDKC_FAILURE) + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): ERROR in reading camera firmware name\n", __FILE__, __LINE__); + + int ret1 = getCameraVersionNum(ver_num); + if (ret1 == RDKC_FAILURE) + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): ERROR in reading camera version num\n", __FILE__, __LINE__); + + unsigned char macaddr[MAC_ADDR_LEN]; + + if (0 == get_mac_address(macaddr)) { + memset(mac_string, 0, sizeof(mac_string)); + transcode_mac_to_string_by_separator(macaddr, '\0', mac_string, XFINITY_MAC_STRING_LEN+1, 0); + } + else { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): ERROR in reading camera mac address\n", __FILE__, __LINE__); + strcpy(mac_string,"No MACADDR"); + } + + return thumbnailUpload; +} + +/** @description Thumbnail Upload Signal handler. + * + * @param s signal + * + * @return void. + */ +void ThumbnailUpload::TNUSignalHandler(int s) +{ + if (SIGTERM == s) + { + term_flag = 1; + } + else if (SIGINT == s) + { + term_flag = 1; + } + else if (SIGUSR1 == s) + { + } +} + +/** @description Register Thumbnail Upload Signal handler. + * @param void. + * @return void. + */ +void ThumbnailUpload::TNURegisterSignalHandler(void) +{ + signal(SIGTERM, TNUSignalHandler); + signal(SIGINT, TNUSignalHandler); + signal(SIGUSR1, TNUSignalHandler); +} + +/** @description: This function is to set thumbnail upload active interval from user config file + * + * @param: void. + * + * @return: void. + * + */ +void ThumbnailUpload::setActiveInterval(void) +{ + time_t currentTime; + char thumbnail_upload_interval[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH]; + char duration[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH]; + char* thumbnail_upload_interval_value = NULL ; + char* duration_value = NULL; + + currentTime = sc_linear_time(NULL); + + // Reset the upload count only if the mode moves from passive to active. + if(false == isActiveInterval) { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Moving to Active mode. Resetting the Upload Count to 0\n", __FILE__, __LINE__); + activeModeUploadCounter = 0; + } + else { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Already in Active mode. Upload count %d\n", __FILE__, __LINE__, activeModeUploadCounter); + } + + memset(thumbnail_upload_interval,0,THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH); + if(RDKC_SUCCESS != rdkc_get_user_setting(THUMBNAIL_UPLOAD_ACTIVE_INTERVAL, thumbnail_upload_interval)) + { + thumbnail_upload_interval_value = (char*)rdkc_envGet(THUMBNAIL_UPLOAD_ACTIVE_INTERVAL); + } + else + { + thumbnail_upload_interval_value = thumbnail_upload_interval; + } + + if (NULL != thumbnail_upload_interval_value) + { + tn_upload_interval = atoi(thumbnail_upload_interval_value); + tn_upload_interval = (0 >= tn_upload_interval) ? DEFAULT_THUMBNAIL_UPLOAD_ACTIVE_INTERVAL: tn_upload_interval; + } + + memset(duration,0,THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH); + if(RDKC_SUCCESS != rdkc_get_user_setting(THUMBNAIL_ACTIVE_UPLOAD_DURATION, duration)) + { + duration_value = (char*)rdkc_envGet(THUMBNAIL_ACTIVE_UPLOAD_DURATION); + } + else + { + duration_value = duration; + } + + if(NULL != duration_value) + { + activeUploadDuration = atoi(duration_value) + currentTime; + } + isActiveInterval = true; + +} + +/** @description: This function is to update thumbnail upload active upload duration + * + * @param: void. + * + * @return: void. + * + */ +int ThumbnailUpload::updateActiveUploadDuration() +{ + time_t currentTime = 0; + char *duration = NULL; + int ret = RDKC_SUCCESS; + int uploadDuration = 0; + char* duration_value = NULL; + currentTime = sc_linear_time(NULL); + uploadDuration = activeUploadDuration - currentTime; + + duration = (char*)malloc(SIZE); + memset(duration,0,SIZE); + + sprintf(duration, "%d", uploadDuration); + if (-1 < uploadDuration) + { + if(RDKC_SUCCESS != rdkc_set_user_setting(THUMBNAIL_ACTIVE_UPLOAD_DURATION, duration)) + { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to release the paramters\n", __FILE__, __LINE__); + ret = RDKC_FAILURE; + } + } + else + { + if(RDKC_SUCCESS == rdkc_set_user_setting(THUMBNAIL_ACTIVE_UPLOAD_DURATION, "0")) + { + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Settng back the Active Upload Duration as zero.\n", __FILE__, __LINE__); + } + isActiveInterval = false; + activeModeUploadCounter = 0; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Moving to Passive mode. Resetting Active mode Upload count to 0 \n",__FILE__, __LINE__); + } + + if(NULL != duration) + { + free(duration); + duration = NULL; + } + + return ret; + +} + +/** @description: This function is to get thumbnail upload attribute from user config file + * + * @param: void. + * + * @return: void. + * + */ +void ThumbnailUpload::getTNUploadAttr() +{ + char thumbnail_upload_interval[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH]; + char thumbnail_enable[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH]; + char *thumbnail_upload_interval_value = NULL; + char *thumbnail_enable_value = NULL; + char thumbnail_upload_server_url[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH]; + char *thumbnail_upload_server_url_value = NULL; + + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s: %d: json_prov_tn_upload_enabled = %d Thumbnail Upload parameters : URL = %s, Interval = %d, Enabled = %s\n" + , __FILE__, __LINE__,json_prov_tn_upload_enabled, tn_upload_server_url, tn_upload_interval, tn_upload_enable ? "true":"false"); + + if(RDKC_SUCCESS != polling_config_init()) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): polling config init failure.\n", __FILE__, __LINE__); + } else { + if(liveCacheConf) { + memset(liveCacheConf, 0, sizeof(livecache_provision_info_t)); + /* Read live cache config */ + if (RDKC_SUCCESS != readLiveCacheConfig(liveCacheConf)) { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Error reading livecache.conf.\n", __FILE__, __LINE__); + } else { + json_prov_tn_upload_enabled = atoi(liveCacheConf->enable); + /* Check thumbnail upload is enabled */ + if(json_prov_tn_upload_enabled) { + memset(tn_upload_server_url, 0, sizeof(tn_upload_server_url)); + memset(tn_upload_auth_token, 0, sizeof(tn_upload_auth_token)); + //strcpy(tn_upload_server_url, liveCacheConf->url); + //strcpy(tn_upload_auth_token, liveCacheConf->auth_token); + strncpy(tn_upload_server_url, liveCacheConf->url, strlen(liveCacheConf->url)); + tn_upload_server_url[strlen(liveCacheConf->url)] = '\0'; + strncpy(tn_upload_auth_token, liveCacheConf->auth_token, strlen(liveCacheConf->auth_token)); + tn_upload_auth_token[strlen(liveCacheConf->auth_token)] = '\0'; + tn_upload_enable = true; + } else { + tn_upload_enable = false; + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Thumbnail is disabled from json config.\n", __FILE__, __LINE__); + } + } + polling_config_exit(); + } + } + + memset(thumbnail_upload_interval,0,THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH); + if(RDKC_SUCCESS != rdkc_get_user_setting((isActiveInterval ? THUMBNAIL_UPLOAD_ACTIVE_INTERVAL:THUMBNAIL_UPLOAD_PASSIVE_INTERVAL), thumbnail_upload_interval)) + { + thumbnail_upload_interval_value = (char*)rdkc_envGet((isActiveInterval ? THUMBNAIL_UPLOAD_ACTIVE_INTERVAL:THUMBNAIL_UPLOAD_PASSIVE_INTERVAL)); + } + else + { + thumbnail_upload_interval_value = thumbnail_upload_interval; + } + + if (NULL != thumbnail_upload_interval_value) + { + tn_upload_interval = atoi(thumbnail_upload_interval_value); + tn_upload_interval = (0 >= tn_upload_interval) ? (isActiveInterval ? DEFAULT_THUMBNAIL_UPLOAD_ACTIVE_INTERVAL : DEFAULT_THUMBNAIL_UPLOAD_PASSIVE_INTERVAL): tn_upload_interval; + } + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s: %d: Thumbnail Upload parameters : URL = %s, Interval = %d, Enabled = %s\n" + , __FILE__, __LINE__,tn_upload_server_url, tn_upload_interval, tn_upload_enable ? "true":"false"); +} + +/** + * @description: This function is used to post the file to the http server. + * + * @param[in]: Path of the file to be uploaded, file length. + * @param[out]: response code. + * + * @return: Error code. + */ +int ThumbnailUpload::postFileToTNUploadServer(char *file_path, int file_len, char* server_url, long *response_code) +{ + int ret = TN_UPLOAD_FAIL; + int file_fd = 0; + int read_len = 0; + char read_buf[THUMBNAIL_UPLOAD_SEND_LEN]; + //struct timeval beforeUpload; + //struct timeval afterUpload; + struct timespec beforeUpload; + struct timespec afterUpload; + long int uploadDuration = 0; + char *data=NULL; + char *ptr = NULL; + int curlCode = 0; + + if (NULL == file_path) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Invalid file name!\n", __FILE__, __LINE__); + return TN_UPLOAD_FAIL; + } + + file_fd = open(file_path, O_RDONLY); + if (file_fd <= 0) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to Open File :%s !\n", __FILE__, __LINE__, file_path); + return TN_UPLOAD_FAIL; + } + + data =(char*)malloc(file_len*sizeof(char)); + if(NULL == data) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to allocate memory :%s !\n", __FILE__, __LINE__, file_path); + close(file_fd); + return TN_UPLOAD_FAIL; + } + memset(data,0,file_len); + memset(read_buf,0,THUMBNAIL_UPLOAD_SEND_LEN); + ptr=data; + + while((read_len = read(file_fd, read_buf, sizeof(read_buf))) > 0) + { + memcpy(ptr, read_buf, read_len); + ptr += read_len; + memset(read_buf,0,THUMBNAIL_UPLOAD_SEND_LEN); + } + + if(NULL == http_client) + { + RDK_LOG(RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Data post Failed - http client is null .\n", __FILE__, __LINE__); + ret = TN_UPLOAD_FAIL; + } + else + { + /* Time Just before Upload */ + //gettimeofday(&beforeUpload, NULL); + clock_gettime(CLOCK_REALTIME, &beforeUpload); + /*Uploading the file */ + curlCode = http_client->post_binary((char*)server_url,(char*)data, response_code, file_len); + + /* Time Just after Upload */ + //gettimeofday(&afterUpload, NULL); + clock_gettime(CLOCK_REALTIME, &afterUpload); + uploadDuration = (afterUpload.tv_sec - beforeUpload.tv_sec)*1000 + ( afterUpload.tv_nsec - beforeUpload.tv_nsec)/1000000; + + if ((*response_code >= RDKC_HTTP_RESPONSE_OK) && (*response_code < RDKC_HTTP_RESPONSE_REDIRECT_START)) + { + if (0 == uploadRetryCount ) { + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Data post Successfully, Response Code : %ld, Upload Duration(in ms) : %ld\n", __FILE__, __LINE__,*response_code, uploadDuration); + } else { + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Data post Successfully after retry, Response Code : %ld, Upload Duration(in ms) : %ld Retry Count: %d\n", __FILE__, __LINE__,*response_code, uploadDuration, uploadRetryCount); + } + curl_off_t uploadSpeed = http_client->getUploadSpeed(); + curl_off_t u1 = 0; + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","Average download speed: %" CURL_FORMAT_CURL_OFF_T " kbyte/sec and m_count = %d \n", uploadSpeed,m_count); + + m_smVector.push_back(uploadSpeed); + if(m_count > SMA_FACTOR) + { + u1 = m_smVector.front(); + m_smVector.erase (m_smVector.begin()); + RDK_LOG(RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","front %" CURL_FORMAT_CURL_OFF_T " kbyte/sec.\n", u1); + m_avgUploadSpeed = m_avgUploadSpeed + uploadSpeed / (curl_off_t)SMA_FACTOR - u1 /(curl_off_t)SMA_FACTOR; + } + else + { + if(m_count == SMA_FACTOR) + { + curl_off_t sum = 0; + + for (int i = 0; i < m_smVector.size(); i++) + { + RDK_LOG(RDK_LOG_DEBUG ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): m_smVector[%d]= %" CURL_FORMAT_CURL_OFF_T " kbyte/sec\n", __FILE__, __LINE__,i,m_smVector[i]); + sum += m_smVector[i]; + } + m_avgUploadSpeed = sum / (curl_off_t)SMA_FACTOR; + //RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): 1st m_avgUploadSpeed after last 10 speeds %" CURL_FORMAT_CURL_OFF_T " bytes/sec\n", __FILE__, __LINE__,m_avgUploadSpeed); + } + } + + if(m_avgUploadSpeed > 0) + { + FILE* fpUsr=NULL; + if((fpUsr=fopen("/tmp/.upstream","w"))!=NULL) + { + /*write the value to the user config file*/ + fprintf(fpUsr,"%" CURL_FORMAT_CURL_OFF_T "\n",m_avgUploadSpeed); + fclose(fpUsr); + } + } + + + + if(m_count <= SMA_FACTOR) + m_count++; + + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","Simple Moving Average Upload speed:%" CURL_FORMAT_CURL_OFF_T "\n", m_avgUploadSpeed); + ret = TN_UPLOAD_OK; + } + else + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Data post Failed. Response code = %ld : curl code = %d\n", __FILE__, __LINE__, *response_code,curlCode); + ret = TN_UPLOAD_FAIL; + } + } + if(NULL != data) + { + free(data); + data = NULL; + } + close(file_fd); + + return ret; +} + +/** + * @description: This function is used to free memory. + * + * @param: void + * + * @return: void. + */ +void ThumbnailUpload::releaseResources() +{ + if(NULL != tn_upload_file_name) + { + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Remove file [%s] from camera ram.\n", __FILE__, __LINE__, tn_upload_file_name); + unlink(tn_upload_file_name); // Remove the file at the end + } + +} + +/** + * @description: This function is used to exit thumbnail upload process. + * + * @param: void + * + * @return: void. + */ +void ThumbnailUpload::TNUExit() +{ + //unlink(LOCK_FILENAME_TNU); + releaseResources(); +} + +/** + * @description: This function is used to call the function which upload data to the http server. + * + * @param[in]: File path,start time,end time, event type,event date time, m file path, motion level, num of arguments. + * + * @return: Error code. + */ +int ThumbnailUpload::uploadThumbnailImage() +{ + int ret = TN_UPLOAD_OK; + int file_len = 0; + struct stat file_stat; + char pack_head[THUMBNAIL_UPLOAD_SEND_LEN+1]; + unsigned char macaddr[MAC_ADDR_LEN]; + char url_string[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH+1]; + long response_code = 0; + int ret_jpeg = 0; + + + /* As customer's requirement, not support http */ + if (!strncasecmp(tn_upload_server_url,"http://", strlen("http://"))) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Invalid upload url [%s], not support http!\n",__FILE__, __LINE__, tn_upload_server_url); + ret = TN_UPLOAD_FAIL; + releaseResources(); + return ret; + } + +#ifdef SUPPORT_IMAGETOOLS + /* Generate thumbnail image using imagetool(OpenCV) utility */ + ret_jpeg = system(cmd); + if(-1 == ret_jpeg) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): GenerateThumbnail failed!\n", __FILE__, __LINE__); + ret = TN_UPLOAD_FAIL; + releaseResources(); + return ret; + } + +#else + /* Generate thumbnail image using snapshooter utility */ + snprintf(cmd,sizeof(cmd)-1, "snapshooter -f %s %s >/dev/null 2>/dev/null", gcpThumbnailSnapshotPath, gcpSnapshooterOpt); + + ret_jpeg = system(cmd); + if(-1 == ret_jpeg) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): system call - snapshooter failed!\n", __FILE__, __LINE__); + ret = TN_UPLOAD_FAIL; + releaseResources(); + return ret; + } +#endif + + if(NULL == tn_upload_file_name) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Invalid file\n", __FILE__, __LINE__); + ret = TN_UPLOAD_FAIL; + releaseResources(); + return ret; + } + + /* get file attribute */ + if (stat(tn_upload_file_name, &file_stat) < 0) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): invalid file [%s], errmsg=%s!\n", + __FILE__, __LINE__, tn_upload_file_name, strerror(errno)); + ret = TN_UPLOAD_FAIL; + releaseResources(); + return ret; + } + file_len = file_stat.st_size; + + /*Adding camera mac in the Server URL */ + memset(url_string, 0, sizeof(url_string)); + snprintf(url_string, THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH, "%s/%s/thumbnail", tn_upload_server_url, mac_string); + + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Post file %s to %s with size : %d\n", __FILE__, __LINE__, tn_upload_file_name, url_string, file_len); + + http_client->resetHeaderList(); + + //logging few chars of token being sent for triage + if(0 != strlen(tn_upload_auth_token)) { + printf("Auth token begins with (%.10s) and ends with (%s)\n", tn_upload_auth_token,tn_upload_auth_token + strlen(tn_upload_auth_token) - 5); + } + + /* Adding Header */ + http_client->addHeader( "Expect", ""); //removing expect header condition by explicitly setting Expect header to "" + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "%s", tn_upload_auth_token); + http_client->addHeader( "Authorization", pack_head); + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "image/jpeg"); + http_client->addHeader( "Content-Type", pack_head); + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "%d",file_len); + http_client->addHeader( "Content-Length", pack_head); + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "%s", mac_string); + http_client->addHeader( "X-Device-MAC", pack_head); + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "%s", "thumbnail"); + http_client->addHeader( "X-Upload-Type", pack_head); + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "%s",DEFAULT_THUMBNAIL_UPLOAD_RESOLUTION); + http_client->addHeader( "X-Image-Resolution", pack_head); + memset(pack_head, 0, sizeof(pack_head)); + snprintf(pack_head, sizeof(pack_head), "%" CURL_FORMAT_CURL_OFF_T "",m_avgUploadSpeed); + http_client->addHeader( "X-Upload-Speed", pack_head); + memset(pack_head, 0, sizeof(pack_head)); +#ifdef USE_MFRLIB + char modelName[128]; + mfrSerializedData_t stdata = {NULL, 0, NULL}; + mfrSerializedType_t stdatatype = mfrSERIALIZED_TYPE_MODELNAME; + if(mfrGetSerializedData(stdatatype, &stdata) == mfrERR_NONE) + { + strncpy(modelName,stdata.buf,stdata.bufLen); + modelName[stdata.bufLen] = '\0'; + RDK_LOG( RDK_LOG_DEBUG,"LOG.RDK.THUMBNAILUPLOAD","%s(%d):Model Name = %s,%s,%d\n",__FILE__, __LINE__,modelName,stdata.buf,stdata.bufLen); + if (stdata.freeBuf != NULL) + { + stdata.freeBuf(stdata.buf); + stdata.buf = NULL; + } + snprintf(pack_head, sizeof(pack_head), "Sercomm %s %s %s %s", modelName, fw_name, mac_string, ver_num); + } + else + { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d):GET ModelName failed : %d\n", __FILE__, __LINE__); + } +#else + + snprintf(pack_head, sizeof(pack_head), "Sercomm %s %s %s %s", SC_MODEL_NAME, fw_name, mac_string, ver_num); +#endif + http_client->addHeader( "User-Agent", pack_head); + + /* Send file to server */ + ret = postFileToTNUploadServer(tn_upload_file_name, file_len, url_string, &response_code); + + if(TN_UPLOAD_OK == ret) { + if(true == isActiveInterval) { + activeModeUploadCounter += 1; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Active mode, Upload Count %d\n",__FILE__, __LINE__,activeModeUploadCounter); + } + } + + releaseResources(); + return ret; +} + +/** @description:Callback function for the message received + * @param[in] hdr : pointer to rtMessage Header + * @param[in] buff : buffer for data received via rt message + * @param[in] n : number of bytes received + * @return: void + */ +void ThumbnailUpload::dynLogOnMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + char const* module = NULL; + char const* logLevel = NULL; + + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + //Handle the rtmessage request + if (rtMessageHeader_IsRequest(hdr)) + { + char* tmp_buff = NULL; + uint32_t tmp_buff_length = 0; + + rtMessage_ToString(req, &tmp_buff, &tmp_buff_length); + rtLog_Info("Req : %.*s", tmp_buff_length, tmp_buff); + free(tmp_buff); + + rtMessage_GetString(req, "module", &module); + rtMessage_GetString(req, "logLevel", &logLevel); + + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.DYN/AMICLOG","(%s):%d Module name: %s\n", __FUNCTION__, __LINE__, module); + RDK_LOG(RDK_LOG_INFO,"LOG.RDK.DYNAMICLOG","(%s):%d log level: %s\n", __FUNCTION__, __LINE__, logLevel); + + RDK_LOG_ControlCB(module, NULL, logLevel, 1); + + // create response + rtMessage res; + rtMessage_Create(&res); + rtMessage_SetString(res, "reply", "Success"); + rtConnection_SendResponse(con, hdr, res, 1000); + rtMessage_Release(res); + } + rtMessage_Release(req); +} + +void ThumbnailUpload::onMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure) +{ + rtConnection con = (rtConnection) closure; + + rtMessage req; + rtMessage_FromBytes(&req, buff, n); + + if (rtMessageHeader_IsRequest(hdr)) + { + char* tmp_buff = NULL; + uint32_t tmp_buff_length = 0; + + rtMessage_ToString(req, &tmp_buff, &tmp_buff_length); + rtLog_Debug("Req : %.*s", tmp_buff_length, tmp_buff); + free(tmp_buff); + + // create response + rtMessage res; + rtMessage_Create(&res); + rtMessage_SetString(res, "reply", "Success"); + setActiveInterval(); + rtConnection_SendResponse(con, hdr, res, 1000); + rtMessage_Release(res); + } + rtMessage_Release(req); + kill(getpid(), SIGUSR1); // Sending signal to own process to wake up from the existing sleep +} + + +void* ThumbnailUpload::rtMessage_Receive(void* arg) +{ + while (1) + { + rtError err = rtConnection_Dispatch(con); + if (err != RT_OK) + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Dispatch Error: %s", __FILE__, __LINE__, rtStrError(err)); + } +} + + +/** @description: Thread function to continuously do thumbnail upload on configured interval + * + * @param: void. + * + * @return: void. + */ +void *ThumbnailUpload::doTNUpload() +{ + time_t start_upload_time = 0; + time_t start_active_time = 0; + time_t current_time = 0; + time_t waitingInterval = 0; + time_t durationLeft = 0; + static int tn_active_uploadfailcount=0; + static int tn_passive_uploadfailcount=0; + pid_t pid = 0; + uploadRetryCount = 0; + + while (!term_flag) + { + if(NULL == ThumbnailUpload::getTNUploadInstance()) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to get thumbnail instance!\n", __FILE__, __LINE__); + sleep(1); + continue; + } + + //Getting Upload Attribute + ThumbnailUpload::getTNUploadInstance()->getTNUploadAttr(); + + current_time = sc_linear_time(NULL); + if((0 != start_upload_time) && ((tn_upload_interval + start_upload_time) > current_time)) + { + waitingInterval = ((tn_upload_interval + start_upload_time) - current_time); + durationLeft = waitingInterval; + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Wait till next upload interval, waiting for %d secs\n",__FILE__, __LINE__,waitingInterval); + if (true == isActiveInterval) + { + if(0 != start_upload_time) + { + start_active_time = sc_linear_time(NULL); + while(0 < durationLeft) + { + if(NULL != ThumbnailUpload::getTNUploadInstance()) + { + if (true == isActiveInterval) + { + if(RDKC_SUCCESS != ThumbnailUpload::getTNUploadInstance()->updateActiveUploadDuration()) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to update active upload duration..\n", __FILE__, __LINE__); + } + } + else + { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d):Breaking, Active Upload Duration completed.\n",__FILE__, __LINE__); + start_active_time = 0; + break; + } + } + sleep(1); + current_time = sc_linear_time(NULL); + durationLeft = waitingInterval + start_active_time - current_time; + } + + if(0 != durationLeft) + { + durationLeft = 0; + continue; + } + } + } + else + { + //Sleep with passive interval + sleep(waitingInterval); + } + } + + if(term_flag) { + break; + } + + //Starting Time + start_upload_time = sc_linear_time(NULL); + + if(NULL == ThumbnailUpload::getTNUploadInstance()) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to get thumbnail instance!\n", __FILE__, __LINE__); + sleep(1); + continue; + } + + + /* Check whether thumbnail upload is enabled */ + if (true == tn_upload_enable) + { + time_t upload_start_time; // scope of "upload_start_time" is limited to this block + time_t time_for_upload; + //Reset the retry count + uploadRetryCount = 0; + + /*Uploading the Thumbnail Image*/ + do { + upload_start_time = sc_linear_time(NULL); + + if (TN_UPLOAD_OK != (ThumbnailUpload::getTNUploadInstance())->uploadThumbnailImage()) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Error Upload Thumbnail\n",__FILE__, __LINE__); + if (true == isActiveInterval) + { + tn_active_uploadfailcount++; + if( ACTIVE_TN_THRESHHOLD_COUNT == tn_active_uploadfailcount ) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Threshold limit reached : tn_active_uploadfailcount : %d : sending sigterm \n", __FUNCTION__, __LINE__,tn_active_uploadfailcount); + tn_active_uploadfailcount = 0; + pid = getpid(); + kill(pid, SIGTERM); + } + } + else + { + tn_passive_uploadfailcount++; + if( PASSIVE_TN_THRESHHOLD_COUNT == tn_passive_uploadfailcount ) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Threshold limit reached : tn_passive_uploadfailcount : %d : sending sigterm \n", __FUNCTION__, __LINE__,tn_passive_uploadfailcount); + tn_passive_uploadfailcount = 0; + pid = getpid(); + kill(pid, SIGTERM); + } + } + + uploadRetryCount++; + + // break if "total upload time" including all the retries exceeds the current "upload interval" + if( ((sc_linear_time(NULL) - start_upload_time) >= tn_upload_interval) || (uploadRetryCount > MAX_UPLOAD_RETRY) ) { + break; + } + + // calculate the time taken for the failed upload + time_for_upload = sc_linear_time(NULL) - upload_start_time; + + // Retry should happen for the leftover time + if( (time_for_upload < 0) || (time_for_upload >= MAX_RETRY_SLEEP) ) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Retry Happens after 0 seconds\n",__FILE__, __LINE__); + } + else if( (time_for_upload < MAX_RETRY_SLEEP) ) { + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Retry Happens after %d seconds\n",__FILE__, __LINE__, (MAX_RETRY_SLEEP - time_for_upload) ); + sleep( MAX_RETRY_SLEEP - time_for_upload ); // Retry every 10(MAX_RETRY_SLEEP) seconds + } + } else { + tn_active_uploadfailcount = 0; + tn_passive_uploadfailcount = 0; + uploadRetryCount = 0; + break; + } + } while( (uploadRetryCount <= MAX_UPLOAD_RETRY) && ((sc_linear_time(NULL) - start_upload_time) < tn_upload_interval) ); + + //Reset the retry count + uploadRetryCount = 0; + } + else + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Upload is disable!\n", __FILE__, __LINE__); + (ThumbnailUpload::getTNUploadInstance())->releaseResources(); + } + + if(true == isActiveInterval) + { + start_active_time = sc_linear_time(NULL); + } + } + + return NULL; +} + +/** + * @description: This function is used to get the camera firmware version. + * + * @param[out]: firmware name + * + * @return: Error code. + */ + +int getCameraImageName(char* out) +{ + size_t max_line_length = FW_NAME_MAX_LENGTH; + char *file_buffer; + char *locate_1 = NULL; + FILE* fp; + char* temp = out; + fp = fopen("/version.txt","r"); + if(fp == NULL) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Error in opening version.txt\n", __FILE__, __LINE__); + return RDKC_FAILURE; + } + + file_buffer = (char*)malloc(FW_NAME_MAX_LENGTH + 1); + if(file_buffer == NULL) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): malloc failed\n", __FILE__, __LINE__); + fclose(fp); + return RDKC_FAILURE; + } + while(getline(&file_buffer,&max_line_length,fp) != -1) + { + /* find the imagename string */ + locate_1 = strstr(file_buffer,"imagename"); + if(locate_1) + { + locate_1 += strlen("imagename:"); + /* copy the contents till linefeed */ + while(*locate_1 != '\n') + *out++ = *locate_1++; + free(file_buffer); + fclose(fp); + return RDKC_SUCCESS; + } + } + /* unable to get the image name */ + //WalError("unable to get the image name"); + strcpy(out,"imagename entry not found"); + free(file_buffer); + fclose(fp); + return RDKC_SUCCESS; +} + +/** + * @description: This function is used to get the camera version number. + * + * @param[out]: version number + * + * @return: Error code. + */ + +int getCameraVersionNum(char* out) +{ + size_t max_line_length = VER_NUM_MAX_LENGTH; + char *file_buffer; + char *locate_1 = NULL; + FILE* fp; + char* temp = out; + fp = fopen("/version.txt","r"); + if(fp == NULL) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Error in opening version.txt\n", __FILE__, __LINE__); + return RDKC_FAILURE; + } + + file_buffer = (char*)malloc(VER_NUM_MAX_LENGTH + 1); + if(file_buffer == NULL) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): malloc failed\n", __FILE__, __LINE__); + fclose(fp); + return RDKC_FAILURE; + } + while(getline(&file_buffer,&max_line_length,fp) != -1) + { + /* find the imagename string */ + locate_1 = strstr(file_buffer,"VERSION"); + if(locate_1) + { + locate_1 += strlen("VERSION="); + /* copy the contents till linefeed */ + while(*locate_1 != '\n') + *out++ = *locate_1++; + free(file_buffer); + fclose(fp); + return RDKC_SUCCESS; + } + } + /* unable to get the image name */ + //WalError("unable to get the image name"); + strcpy(out,"No Ver Num"); + free(file_buffer); + fclose(fp); + return RDKC_SUCCESS; +} + + + diff --git a/src/thumbnailUpload.h b/src/thumbnailUpload.h new file mode 100644 index 0000000..964d92e --- /dev/null +++ b/src/thumbnailUpload.h @@ -0,0 +1,170 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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. +########################################################################## +*/ +#ifndef _THUMBNAIL_UPLOAD_H_ +#define _THUMBNAIL_UPLOAD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rdk_debug.h" +#include "HttpClient.h" +#include "RFCCommon.h" +#ifdef __cplusplus +extern "C" { +#endif +#include "dev_config.h" +#include "polling_config.h" +#include "cgi_image.h" +#include "SYS_log.h" +#ifdef USE_MFRLIB +#include "mfrApi.h" +#endif + +#ifdef __cplusplus +} +#endif + + +/* RtMessage */ +#include "rtLog.h" +#include "rtConnection.h" +#include "rtMessage.h" +#define WEBPA_ADDRESS "tcp://127.0.0.1:10001" +#define SMA_FACTOR 10 + +#define RDKC_FAILURE -1 +#define RDKC_SUCCESS 0 + +#define THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH 512 +#define THUMBNAIL_UPLOAD_AUTH_MAX_LENGTH 1024 +#define THUMBNAIL_UPLOAD_SEND_LEN 2048 +#define THUMBNAIL_UPLOAD_MAC_STRING_LEN 12 +#define SIZE 50 + +#define DEFAULT_THUMBNAIL_UPLOAD_ENABLE true +#define DEFAULT_THUMBNAIL_UPLOAD_PASSIVE_INTERVAL 300 +#define DEFAULT_THUMBNAIL_UPLOAD_ACTIVE_INTERVAL 30 +#define DEFAULT_THUMBNAIL_UPLOAD_ACTIVE_DURATION 120 + +//Need to change the value once provided with actual certs, using test certs for now. +#define THUMBNAIL_UPLOAD_CA_CERT_FILE "/etc/ssl/certs/ca-chain.cert.pem" +#define THUMBNAIL_UPLOAD_CERT_FILE "/etc/ssl/certs/testclient1031.cert.pem" +#define THUMBNAIL_UPLOAD_KEY_FILE "/etc/ssl/certs/testclient1031.key.pem" + +#define LOCK_FILENAME_TNU "/tmp/thumbnail_upload.lock" + +#define FW_NAME_MAX_LENGTH 256 +#define VER_NUM_MAX_LENGTH 128 + +//SUPPORT_IMAGETOOLS +#define COMPRESSION_SCALE 40 +#define TN_OP_WIDTH 640 +#define TN_OP_HEIGHT 360 +#define SNAPSHOT_FILE "/opt/tn_snapshot.jpg" +#define PASSIVE_TN_THRESHHOLD_COUNT 6 +#define ACTIVE_TN_THRESHHOLD_COUNT 12 +#define MAX_UPLOAD_RETRY 2 +#define MAX_RETRY_SLEEP 10 +#define MAXSIZE 100 + +#define RTMSG_DYNAMIC_LOG_REQ_RES_TOPIC "RDKC.ENABLE_DYNAMIC_LOG" +#define RTMSG_THUMBNAIL_TOPIC "RDKC.THUMBNAIL" + +int getCameraImageName(char *out); +int getCameraVersionNum(char *out); + +const char gcpThumbnailSnapshotPath[] = "/opt/tn_snapshot.jpg"; +const char gcpSnapshooterOpt[] = " -r 13 -q 3"; +const char DEFAULT_THUMBNAIL_UPLOAD_URL[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH] = "https://livecache-stg-cvr-nc.sys.comcast.net/camera"; +const char DEFAULT_THUMBNAIL_UPLOAD_AUTH_TOKEN[THUMBNAIL_UPLOAD_AUTH_MAX_LENGTH] = ""; +const char DEFAULT_THUMBNAIL_UPLOAD_RESOLUTION[SIZE] = "640x360"; + +enum TN_UPLOAD_STATUS +{ + TN_UPLOAD_OK = 0, + TN_UPLOAD_FAIL, + TN_UPLOAD_CONNECT_ERR, + TN_UPLOAD_SEND_ERR, + TN_UPLOAD_RESPONSE_ERROR, + TN_UPLOAD_TIMEOUT, + TN_UPLOAD_MAX +}; + +class ThumbnailUpload { + +public: + ThumbnailUpload(); + ~ThumbnailUpload(); + void rtConnection_init(); + static ThumbnailUpload *getTNUploadInstance(); + static void *doTNUpload(); + int controlTNUploadProcess(char *process); + bool getTNUploadProvAttr(); + static void TNURegisterSignalHandler(void); + void TNUExit(); + static void* rtMessage_Receive(void * arg); + void rtConnection_destroy(); +private: + void getTNUploadAttr(); + int postFileToTNUploadServer(char * fileName, int fileLen, char* serverUrl, long * responseCode); + int uploadThumbnailImage(); + int checkTNUploadfilelock(char *fname); + int updateActiveUploadDuration(); + //Callback function for topics on thumbnail + static void onMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + //Callback function for topics on dynamic Logging + static void dynLogOnMessage(rtMessageHeader const* hdr, uint8_t const* buff, uint32_t n, void* closure); + static void setActiveInterval(void); + static void TNUSignalHandler(int s); + void releaseResources(); + static ThumbnailUpload* thumbnailUpload; + HttpClient *http_client; + char tn_upload_server_url[THUMBNAIL_UPLOAD_PARAM_MAX_LENGTH]; + char tn_upload_auth_token[THUMBNAIL_UPLOAD_AUTH_MAX_LENGTH]; + int json_prov_tn_upload_enabled; + static int tn_upload_interval; + static bool tn_upload_enable; + livecache_provision_info_t *liveCacheConf; + static volatile sig_atomic_t term_flag; + char* tn_upload_file_name; + static bool isActiveInterval; + static int activeUploadDuration; + static char fw_name[FW_NAME_MAX_LENGTH]; + static char ver_num[VER_NUM_MAX_LENGTH]; + static char mac_string[THUMBNAIL_UPLOAD_MAC_STRING_LEN + 1]; + static rtConnection con; + static unsigned int activeModeUploadCounter; + char cmd[MAXSIZE]; + static int uploadRetryCount; + int m_count; + curl_off_t m_avgUploadSpeed; + std::vector m_smVector; +}; + +#endif diff --git a/src/thumbnail_upload_main.cpp b/src/thumbnail_upload_main.cpp new file mode 100644 index 0000000..edb2f79 --- /dev/null +++ b/src/thumbnail_upload_main.cpp @@ -0,0 +1,89 @@ +/* +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2019 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License 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 "thumbnailUpload.h" + +#define MAXRETRY 5 + +int main(int argc, char *argv[]) +{ + /* ENABLING RDK LOGGER */ + rdk_logger_init("/etc/debug.ini"); + config_init(); + ThumbnailUpload *thumbnail_upload = ThumbnailUpload::getTNUploadInstance(); + + thumbnail_upload->rtConnection_init(); + + pthread_t thumbnailUploadThread; + if( RDKC_SUCCESS != pthread_create(&thumbnailUploadThread, NULL, &ThumbnailUpload::rtMessage_Receive, thumbnail_upload )) + { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): can't create thread. \n", __FILE__, __LINE__ ); + return RDKC_FAILURE; + } + + pthread_setname_np(thumbnailUploadThread,"thumbnail_upload"); + RDK_LOG( RDK_LOG_INFO,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Created thumbnail upload thread. \n", __FILE__, __LINE__ ); + + /* Register signal handler */ + if( NULL != thumbnail_upload) { + thumbnail_upload->TNURegisterSignalHandler(); + } + + int retry = 0; + if( NULL != thumbnail_upload) { + do { + if(!thumbnail_upload->getTNUploadProvAttr()) { + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): Failed to read json thumbnail configuration retrying...\n", __FILE__, __LINE__); + retry++; + if ( retry > MAXRETRY ) { + RDK_LOG( RDK_LOG_ERROR,"LOG.RDK.THUMBNAILUPLOAD","%s(%d) : FATAL : Max retry reached in getTNUploadProvAttr exit process %d\n", __FILE__, __LINE__); + exit(1); + } + sleep(2); + } + else { + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d): success in reading thumbnail configuration from livecache.conf\n", __FILE__, __LINE__); + break; + } + } while ( retry <= MAXRETRY) ; + } + + //Do Thumbnail Upload + if( NULL != thumbnail_upload) { + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d):Starting Thumbnail Upload.\n", __FILE__, __LINE__); + thumbnail_upload->doTNUpload(); + } + + //Exiting Thumbnail Upload + if( NULL != thumbnail_upload) { + thumbnail_upload->rtConnection_destroy(); + thumbnail_upload->TNUExit(); + //pthread_exit(0); + pthread_kill(thumbnailUploadThread, 0); + if( NULL != thumbnail_upload) { + delete thumbnail_upload; + thumbnail_upload = NULL; + } + RDK_LOG(RDK_LOG_INFO ,"LOG.RDK.THUMBNAILUPLOAD","%s(%d):Stopping Thumbnail Upload.\n", __FILE__, __LINE__); + } + + return 0; +}