Last Updated on : 2025-09-30 06:53:07download
Local storage, a basic feature of an IP camera (IPC), manages the SD card. It stores audios, videos, and images to the SD card in the specified directory and file structure, supporting data search, playback, download, and deletion.
Local storage can record live streams to the SD card, allowing for event recording or continuous recording. When the local storage is full, new videos will overwrite the oldest ones. Playback by time allows searching for recordings by time.
Development framework
The general features provided by the IPC Development Framework.
Components
svc_local_storage
The local storage uses a general file system to store audio, video, and index in a proprietary format and images in JPG format. Files are organized with a multilevel directory and file naming convention for fast retrieval.
Directories are broken down into two independent categories: recordings and local albums, as shown below.
DCIM // The storage directory, located in the top-level directory in the SD card file system.
├── CHAN0 // The recording storage directory of the first channel for 1-channel or multichannel IPCs.
│ └── 2022
│ └── 10
│ └── 14
│ ├── 1665732067_0014 // The event folder, containing 14-second footage in UTC.
│ │ ├── 0000.media // The audio and video file in Tuya's proprietary format, starting at 0 seconds.
│ │ ├── 0010.media // Start at 10 seconds.
│ │ ├── 1665732067.jpg // The thumbnail of an event video.
│ │ └── .info // Information about the video format and event type.
│ └── day_idx.bin // The index of the .info file.
├── CHAN1
│ ... // The recording storage directory of the second channel for multichannel IPCs, with the same structure as that of the first channel.
There are three recording modes:
Not recording (at any time)
Continuous recording (24/7)
Event recording (Recording is triggered by events. Pre-recording is supported.)
Pre-recording enables storing the footage N seconds before the event. The maximum value of N is limited by ringBuff. For example, if ringBuff is 10 seconds, the upper limit of N is 9 seconds.
When the available space on the SD card is less than 500 MB, the program will automatically delete the oldest recordings to make room for new recordings. Only one event folder is deleted at a time.
Read audio and video from the SD card and selectively send the data to the client at a specific rate.
Read audio and video from the SD card and send the data to the client as fast as possible.
Delete the recording on a specific day after the user initiates the deletion on the mobile app.
When storage is enabled and continuous recording or event recording is selected, the audio and video streams are automatically recorded to the SD card in Tuya’s proprietary format by default. Therefore, the video files saved in the SD card cannot be opened and played by conventional players. Users can only view playback on the Tuya-enabled app, which meets security requirements.
To meet higher security requirements, the stored data is encrypted based on the proprietary format. Invoke the specified API to turn the encryption feature on or off. After the encryption feature is turned on, the stream data will be encrypted and written to the SD card.
The encrypted video can only be played back on the device used when recording. If the SD card is inserted into other devices, the encrypted video cannot be played back properly. For example, the encrypted video recorded in device A cannot be played back properly in device B.
The MP4 file format is a digital multimedia container format widely used for storing video, audio, subtitles, and images. MP4, short for MPEG-4 Part 14, is based on the ISO/IEC 14496-12 standard. It is a highly compatible video format that supports multiple encoding standards.
Available MP4 video encodings of the latest SDK include H.264 and H.265, while audio encodings include PCM and G.711.
After MP4 storage is enabled, it will no longer be compatible with proprietary format recordings. Additionally, the video encryption feature will no longer be supported.
typedef struct {
CHAR_T base_path[SS_BASE_PATH_LEN]; // The root directory of video storage.
UINT_T max_event_per_day; // The maximum number of events per day, which is based on the SoC capabilities. Query and get the playback information within the acceptable time period. If the upper limit is exceeded, a query message will be rejected.
SS_SD_STATUS_CHANGED_CB sd_status_changed_cb; // The callback invoked when the storage card status changes.
INT_T stor_format; // 0: Default, storage in proprietary format. 1: storage in MP4 format (encryption not supported).
INT_T encrypt_mode; // 1: Enable encryption.
INT_T security_level; // Encryption level (optional).
UINT_T skills; // 0 means all the capabilities are supported. See TUYA_IPC_SKILL_BASIC | TUYA_IPC_SKILL_DOWNLOAD for details.
TUYA_IPC_ALBUM_INFO_T album_info; // Album initialization parameters.
INT_T max_event_duration; // Maximum event duration, 0 defaults to 10 minutes.
UINT_T write_card_period_s; // Video data write cycle, 0 defaults to writing data every 6 seconds.
TUYA_IPC_STORAGE_WORKING_PATH_NAME_T path_name; // Custom path naming for video storage.
TUYA_IPC_AOV_STORAGE_INFO_T aov_info; // AOV storage parameters.
} TUYA_IPC_STORAGE_VAR_T;
SD card status callback.
typedef OPERATE_RET (*SS_SD_STATUS_CHANGED_CB)(SD_STATUS_E status);
Parameter description
typedef enum {
SD_STATUS_UNKNOWN = 0,
SD_STATUS_NORMAL,
SD_STATUS_ABNORMAL,
SD_STATUS_LACK_SPACE,
SD_STATUS_FORMATING,
SD_STATUS_NOT_EXIST,
SD_STATUS_MAX
} SD_STATUS_E;
The SDK integrates internal logic for SD card status monitoring and status change notifications.
Due to differences across development platforms, the SDK relies on the following developer-implemented interfaces: SD_STATUS_E tuya_ipc_sd_get_status(VOID) to get the card’s status, and VOID tuya_ipc_sd_get_capacity_by_mount_path(CONST CHAR_T* mount_path, UINT_T *p_total, UINT_T *p_used, UINT_T *p_free) to get the card’s remaining capacity.
The user initiates formatting on the mobile app to erase the SD card for normal usage.
You need to implement the formatting process and strictly refer to the processing logic in the demo.
Due to variations in app versions, it is recommended to report the card status DP as SD_STATUS_FORMATTING immediately before initiating formatting. This helps prevent issues such as the formatting progress bar failing to display.
After retrieving the SD card’s status and remaining capacity, the SDK performs a comprehensive assessment to determine its overall status. If an exception is detected, the SDK will automatically stop recording and notify you of the status via a callback function. If the card’s status subsequently returns to normal, the SDK will automatically resume recording based on the current recording mode configuration.
The recording mode configuration must still be obtained through STREAM_STORAGE_WRITE_MODE_E tuya_ipc_sd_get_mode_config(), which is implemented by you.
Initialize the storage channel, which will create and initialize a buffer. OPRT_OK is returned on success.
/**
* @brief Initialize local storage
*
* @param[in] p_storage_var: pointer
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_init(IN TUYA_IPC_STORAGE_VAR_T *p_storage_var);
Perform deinitialization to free the buffer memory.
/**
* @brief Uninit tuya stream storage, free used resource, and memory.
*
* @param VOID
*
* @return VOID
*/
VOID tuya_ipc_ss_uninit(VOID);
/**
* @brief Check if stream storage is inited
*
* @param VOID
*
* @return TRUE/FALSE
*/
BOOL_T tuya_ipc_ss_is_inited(VOID);
/**
* @brief Set write mode of stream storage
*
* @param[in] write_mode
*
* @return OPERATE_RET
*/
OPERATE_RET tuya_ipc_ss_set_write_mode(IN CONST STREAM_STORAGE_WRITE_MODE_E write_mode);
/**
* @brief Get current stream storage write mode
*
* @param VOID
*
* @return STREAM_STORAGE_WRITE_MODE_E
*/
STREAM_STORAGE_WRITE_MODE_E tuya_ipc_ss_get_write_mode(VOID);
/**
* @brief Check if stream storage write mode is enabled
*
* @param VOID
*
* @return TRUE/FALSE
*/
BOOL_T tuya_ipc_ss_write_mode_is_enabled(VOID);
/**
* @brief Start event stream storage by channel
*
* @param[in] channel: which channel, 0 for ipc
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_start_event(IN INT_T channel);
This API only generates video clips in event recording mode and does not record event details.
/**
* @brief Stop event stream storage by channel
*
* @param[in] channel: which channel, 0 for ipc
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_stop_event(IN INT_T channel);
/**
* @brief Create and start event stream storage
*
* @param[in] channel: which channel, default: 0(camera0)
* @param[in] event_info: event info
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_start_event_by_info(IN INT_T channel, IN SS_EVENT_DETAIL_INFO_T* event_info);
This API was added in v3.10.16 to enable event recording and log event details, such as event type and captured images.
/**
* @brief Create and start event stream storage
*
* @param[in] channel: which channel, default: 0(camera0)
* @param[in] event_info: event info
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_start_event_by_info(IN INT_T channel, IN SS_EVENT_DETAIL_INFO_T* event_info);
This API was added in v3.10.16 to append the event information to the recording after event recording is enabled. It can only be used after tuya_ipc_ss_start_event_by_info is invoked.
/**
* @brief Stop event stream storage after tuya_ipc_ss_start_event_by_info
*
* @param[in] channel: which channel, default: 0(camera0)
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_stop_event_by_info(IN INT_T channel);
This API was added in v3.10.16 to stop recordings generated by tuya_ipc_ss_start_event_by_info.
TUYA_IPC_SKILL_SUPPORT_PB_V21 capability flag must be added during initialization.MEDIA_STREAM_PLAYBACK_QUERY_DAY_V3 command in the new demo./**
* @brief Set pre-record time, invoke this API only if needed, or it is 2 seconds by default.
* Should be invoked once after init and before start.
*
* @param[in] pre_record_second: time in seconds
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_set_pre_record_time(IN UINT_T pre_record_second);
/**
* @brief Set max event duration, invoke this API only if needed, or it is 600 seconds by default.
* Should be invoked once after init and before start.
*
* @param[in] max_event_duration: time in seconds
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_set_max_event_duration(IN UINT_T max_event_duration);
This API is invoked based on the MEDIA_STREAM_PLAYBACK_START_TS P2P event. For more information, see the demo application.
/**
* @brief Start a new playback
*
* @param[in] pb_idx: the playback and search index, used for simultaneous search from different clients
* @param[in] event_cb: callback function for playback events
* @param[in] video_cb: callback function for getting playback video data
* @param[in] audio_cb: callback function for getting playback audio data
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_pb_start(IN UINT_T pb_idx, IN SS_PB_EVENT_CB event_cb, IN SS_PB_GET_MEDIA_CB video_cb, IN SS_PB_GET_MEDIA_CB audio_cb);
Callback parameters
typedef VOID (*SS_PB_EVENT_CB)(IN UINT_T pb_idx, IN SS_PB_EVENT_E pb_event, IN PVOID_T args);
Playback event callback
typedef VOID (*SS_PB_GET_MEDIA_CB)(IN UINT_T pb_idx, IN CONST MEDIA_FRAME_T *p_frame);
Callback parameters of audio and video receiving
| Parameter | Description |
|---|---|
pb_idx |
The playback and search index, used for simultaneous search from different clients. |
event_cb |
Playback event callback. |
video_cb |
Video data callback. |
audio_cb |
Audio data callback. |
Stop playback on the specified channel.
/**
* @brief Stop an ongoing playback
*
* @param[in] pb_idx: playback/query index, for different clients, do query at the same time
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_pb_stop(IN UINT_T pb_idx);
tuya_ipc_ss_init is invoked to initialize local storage, set the encryption enable parameter./**
* @brief Set the encryption mode
*
* @param[in] encrypt_enable: turn encryption on or off
*
* @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
*/
OPERATE_RET tuya_ipc_ss_set_encrypt_mode(IN CONST BOOL_T encrypt_enable)
Set the stor_format parameter to 1 in the tuya_ipc_ss_init initialization function to enable storage in MP4 format.
After this feature is enabled, the storage path differs from that of the proprietary format. Therefore, playback of previously recorded proprietary-format videos is no longer supported.
The recording buffer memory is allocated when a normal SD card is detected and deallocated when local storage is deinitialized.
Memory size: N seconds × (the size of 1-second main video stream + the size of 1-second main audio stream). N is configurable, defaulting to 6.
For multichannel recording, each channel is allocated a buffer.
Audio and video data is stored in Tuya’s proprietary format, with the suffix .media.
If you prefer a custom storage logic instead of the standard implementation, you can choose not to enable local storage. Instead, use your own storage program for the customized directory structure, file size and format, and read/write solution. When integrating video playback, downloads, and other features, you must still follow the standard protocol.
This issue is common in low-power devices, where tuya_ipc_ss_stop_event is executed immediately after tuya_ipc_ss_start_event during the initialization phase.
Due to the characteristics of low-power devices, when calling tuya_ipc_ss_start_event, the initialization of the local storage module may not be completed. At this time, event markers will be cached internally. After the initialization is completed, recording will start based on the event markers. If tuya_ipc_ss_stop_event is called during this period, the markers will be cleared, and eventually, no event recording will be generated.
Solution:
When you need to start storing event recordings, use tuya_ipc_ss_is_inited to determine the current initialization status before calling interfaces such as start. At the same time, try to avoid excessively short intervals between interface calls.
The problem that recording data cannot be saved during initialization can be solved by extending the pre-recording duration, etc.
After tuya_ipc_ss_start_event or tuya_ipc_ss_set_write_mode is called, a not init error appears in the log, indicating that the directory of the storage module has not been initialized.
This issue is mostly related to the implementation of the upper layer SD_STATUS_E tuya_ipc_sd_get_status(VOID). The SDK will call tuya_ipc_sd_get_status internally, and the storage directory initialization can only be completed after SD_STATUS_NORMAL is returned.
You can enable the Download and Delete buttons on the playback interface only after uploading the specified capability flag through tuya_ipc_upload_skills. Each capability flag can be set one by one based on the input parameter skills of tuya_ipc_ss_init. You can find the value by searching for TUYA_IPC_SKILL_DOWNLOAD in tuya_ipc_stream_storage.h. Pay attention to the following points:
tuya_ipc_ss_init before calling tuya_ipc_upload_skills. Otherwise, the stored capability flags cannot be uploaded.tuya_ipc_upload_skills after connecting to the internet. Otherwise, the capability flags cannot be uploaded.This issue is related to the parameters used when sending playback audio and video data. In the MP4 solution, when using tuya_ipc_media_playback_send_video_frame_with_encrypt to send video frame data, you must manually set the following encoding parameters in the request parameters, so the app can decode the data correctly: media.video.video_codec, media.video.video_width, and media.video.video_height.
This issue is often related to the implementation of the API VOID tuya_ipc_sd_get_capacity(UINT_T *p_total, UINT_T *p_used, UINT_T *p_free).
The SDK calls this API to get the real-time remaining storage capacity and trigger specific strategies. The circular overwrite logic in the SDK operates as follows:
Yes, it is supported. The prerequisites are that the device must be activated, and both the device and SDK time must be within a normal range. An exceptional time setting might cause issues with the video storage location.
Typically, the SDK automatically synchronizes with cloud time periodically after connecting to the internet. Suppose you prefer to set the SDK time manually instead of using cloud synchronization. In that case, you can disable cloud time sync by calling httpc_update_localtime_set after SDK initialization, and use tuya_ipc_set_service_time to set the SDK time.
This feature only supports video clips generated by tuya_ipc_ss_start_event_by_info. Please also note the following details:
TUYA_IPC_SKILL_SUPPORT_PB_V21 capability flag during tuya_ipc_ss_init.MEDIA_STREAM_PLAYBACK_QUERY_DAY_V3 and MEDIA_STREAM_DOWNLOAD_IMAGE_V2 must be implemented during the playback query. Refer to the demo code for specific implementation examples.ring buffer matches the theoretical expectations. It is recommended to use tuya_ipc_ring_buffer_append_data_with_timestamp. The last parameter timestamp should be assigned using tal_time_get_posix_ms.Make sure the header file "tal_time_service.h" is included in your .c file.
This issue is related to the handling of the delete command. Refer to the processing logic for the MEDIA_STREAM_PLAYBACK_DELETE command in the latest version of the demo code.
A key characteristic of low power devices is that they rapidly generate audio and video frames after power-on and write them to the ring buffer. When these frames are written, the device has most likely not yet synchronized its time information, resulting in abnormal timestamps. The solution is as follows:
tuya_ipc_ring_buffer_correct_timestamp to correct the erroneous timestamps of the frames in the ring buffer.Is this page helpful?
YesFeedbackIs this page helpful?
YesFeedback