Skip to content

Commit f23fedd

Browse files
committed
Merge branch 'feature/add_more_task_loop_and_gmf_cache' into 'main'
Add more loop path for gmf_task and gmf_cache See merge request adf/multimedia/esp-gmf!21
2 parents 656d12f + 765c2bf commit f23fedd

File tree

15 files changed

+1540
-504
lines changed

15 files changed

+1540
-504
lines changed

gmf_core/include/esp_gmf_cache.h

Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
* ESPRESSIF MIT License
3+
*
4+
* Copyright (c) 2025 <ESPRESSIF SYSTEMS (SHANGHAI) CO., LTD>
5+
*
6+
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
7+
* it is free of charge, to any person obtaining a copy of this software and associated
8+
* documentation files (the "Software"), to deal in the Software without restriction, including
9+
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
10+
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished
11+
* to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all copies or
14+
* substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18+
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20+
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
*
23+
*/
24+
25+
#pragma once
26+
27+
#include <string.h>
28+
#include "esp_gmf_oal_mem.h"
29+
#include "esp_gmf_payload.h"
30+
31+
/**
32+
* @brief This GMF payload cache module is designed to cache a data block of a specified size. Each call to acquire returns this
33+
* fixed-size block. If the acquired data is smaller than the expected size, new data must be loaded. When sufficient data
34+
* is available, release clears the cached block.
35+
* It is commonly used in scenarios where an element can only process a fixed data size, but the input data length is variable.
36+
* For example, if an element requires exactly 1350 bytes per processing cycle but receives input of varying lengths, you can
37+
* create a cache with `esp_gmf_cache_new(1350, out_handle)`, and the element can then fetch data using `esp_gmf_cache_acquire`
38+
*/
39+
40+
#ifdef __cplusplus
41+
extern "C" {
42+
#endif /* __cplusplus */
43+
44+
/**
45+
* @brief Structure representing a cache for GMF payloads
46+
*/
47+
typedef struct {
48+
uint8_t *buf; /*!< Pointer to the cache buffer */
49+
uint32_t buf_len; /*!< Total allocated size of the cache buffer */
50+
uint32_t buf_filled; /*!< Amount of data currently stored in the buffer */
51+
esp_gmf_payload_t origin_load; /*!< Original payload from which data is copied */
52+
esp_gmf_payload_t load; /*!< Current payload structure for acquiring data */
53+
} esp_gmf_cache_t;
54+
55+
/**
56+
* @brief Create a new GMF cache instance
57+
*
58+
* @param[in] len The size of the cache buffer to allocate
59+
* @param[out] handle Pointer to the cache handle to be initialized
60+
*
61+
* @return
62+
* - ESP_GMF_ERR_OK Success, cache is initialized
63+
* - ESP_GMF_ERR_INVALID_ARG If `handle` is NULL
64+
* - ESP_GMF_ERR_MEMORY_LACK If memory allocation fails
65+
*/
66+
static inline esp_gmf_err_t esp_gmf_cache_new(uint32_t len, esp_gmf_cache_t **handle)
67+
{
68+
if (handle == NULL) {
69+
return ESP_GMF_ERR_INVALID_ARG;
70+
}
71+
*handle = (esp_gmf_cache_t *)esp_gmf_oal_calloc(1, sizeof(esp_gmf_cache_t));
72+
if (*handle == NULL) {
73+
ESP_LOGE("GMF_CACHE", "Failed to allocate the cache instance");
74+
return ESP_GMF_ERR_MEMORY_LACK;
75+
}
76+
(*handle)->buf = (uint8_t *)esp_gmf_oal_calloc(1, len);
77+
if ((*handle)->buf == NULL) {
78+
esp_gmf_oal_free(*handle);
79+
ESP_LOGE("GMF_CACHE", "Failed to allocate the cache buffer, size:%ld", len);
80+
*handle = NULL;
81+
return ESP_GMF_ERR_MEMORY_LACK;
82+
}
83+
(*handle)->buf_len = len;
84+
(*handle)->buf_filled = 0;
85+
return ESP_GMF_ERR_OK;
86+
}
87+
88+
/**
89+
* @brief Delete a GMF cache instance and free associated memory
90+
*
91+
* @param[in] handle Pointer to the cache handle
92+
*
93+
* @return
94+
* - ESP_GMF_ERR_OK Success, cache is deleted
95+
* - ESP_GMF_ERR_INVALID_ARG If `handle` is NULL
96+
*/
97+
static inline esp_gmf_err_t esp_gmf_cache_delete(esp_gmf_cache_t *handle)
98+
{
99+
if (handle == NULL) {
100+
return ESP_GMF_ERR_INVALID_ARG;
101+
}
102+
if (handle->buf) {
103+
esp_gmf_oal_free(handle->buf);
104+
}
105+
esp_gmf_oal_free(handle);
106+
return ESP_GMF_ERR_OK;
107+
}
108+
109+
/**
110+
* @brief Check if the cache is ready to load new data
111+
* If the return `is_ready` is true, `esp_gmf_cache_load` can be called to load a new payload
112+
*
113+
* @param[in] handle Pointer to the cache handle
114+
* @param[out] is_ready Pointer to a boolean flag indicating whether new data can be loaded
115+
*
116+
* @return
117+
* - ESP_GMF_ERR_OK Success, `is_ready` is set
118+
* - ESP_GMF_ERR_INVALID_ARG If `handle` or `is_ready` is NULL
119+
*/
120+
static inline esp_gmf_err_t esp_gmf_cache_ready_for_load(esp_gmf_cache_t *handle, bool *is_ready)
121+
{
122+
if ((handle == NULL) || (is_ready == NULL)) {
123+
return ESP_GMF_ERR_INVALID_ARG;
124+
}
125+
*is_ready = false;
126+
if (handle->origin_load.valid_size == 0) {
127+
*is_ready = true;
128+
}
129+
return ESP_GMF_ERR_OK;
130+
}
131+
132+
/**
133+
* @brief Load new payload data into the cache
134+
* Call `esp_gmf_cache_ready_for_load` before use to check if it is ready to load a new payload
135+
*
136+
* @param[in] handle Pointer to the cache handle
137+
* @param[in] load_in Pointer to the payload data to be loaded
138+
*
139+
* @return
140+
* - ESP_GMF_ERR_OK Success, payload is loaded
141+
* - ESP_GMF_ERR_INVALID_ARG If `handle` or `load_in` is NULL
142+
* - ESP_GMF_ERR_INVALID_STATE If previous data has not been consumed
143+
*/
144+
static inline esp_gmf_err_t esp_gmf_cache_load(esp_gmf_cache_t *handle, const esp_gmf_payload_t *load_in)
145+
{
146+
if ((handle == NULL) || (load_in == NULL)) {
147+
return ESP_GMF_ERR_INVALID_ARG;
148+
}
149+
if (handle->origin_load.valid_size != 0) {
150+
ESP_LOGE("GMF_CACHE", "Reloading while previous load underuse, call esp_gmf_cache_ready_for_load check firstly, filled: %ld,\
151+
orig_valid: %d", handle->buf_filled, handle->origin_load.valid_size);
152+
return ESP_GMF_ERR_INVALID_STATE;
153+
}
154+
memcpy(&handle->origin_load, load_in, sizeof(esp_gmf_payload_t));
155+
return ESP_GMF_ERR_OK;
156+
}
157+
158+
/**
159+
* @brief Acquire a data chunk of the expected size from the cache
160+
*
161+
* @note
162+
* - This function must be used in conjunction with `esp_gmf_cache_release`
163+
* - The `expected_size` should match the internal buffer length
164+
* - If `expected_size` exceeds the current buffer length, the buffer will be reallocated
165+
*
166+
* @param[in] handle Pointer to the cache handle
167+
* @param[in] expected_size The requested data size in bytes
168+
* @param[out] load_out Pointer to store the acquired payload data
169+
*
170+
* @return
171+
* - ESP_GMF_ERR_OK Data successfully acquired
172+
* - ESP_GMF_ERR_INVALID_ARG If `handle` or `load_out` is NULL
173+
* - ESP_GMF_ERR_MEMORY_LACK Insufficient memory to allocate a new buffer
174+
*/
175+
static inline esp_gmf_err_t esp_gmf_cache_acquire(esp_gmf_cache_t *handle, uint32_t expected_size, esp_gmf_payload_t **load_out)
176+
{
177+
if ((handle == NULL) || (load_out == NULL)) {
178+
return ESP_GMF_ERR_INVALID_ARG;
179+
}
180+
if (expected_size > handle->buf_len) {
181+
uint8_t *buf = (uint8_t *)esp_gmf_oal_realloc(handle->buf, expected_size);
182+
ESP_GMF_MEM_CHECK("GMF_CACHE", buf, return ESP_GMF_ERR_MEMORY_LACK;);
183+
handle->buf = buf;
184+
ESP_LOGI("GMF_CACHE", "Reallocate the cache buffer from %ld to %ld bytes, %p", handle->buf_len, expected_size, handle->buf);
185+
handle->buf_len = expected_size;
186+
}
187+
188+
*load_out = &handle->load;
189+
ESP_LOGD("GMF_CACHE", "ACQ, filled: %ld, Origin_valid_size: %d", handle->buf_filled, handle->origin_load.valid_size);
190+
191+
/*
192+
* 1. If the original buffer has sufficient data, return its address directly to the user
193+
* 2. Copy the remaining data from the original buffer to the cache buffer
194+
* 3. If the original buffer does not have enough data for the user, provide the cached buffer address instead
195+
*/
196+
197+
if (handle->buf_filled == 0) {
198+
if (handle->origin_load.valid_size >= expected_size) {
199+
(*load_out)->buf = handle->origin_load.buf;
200+
(*load_out)->buf_length = expected_size;
201+
(*load_out)->valid_size = expected_size;
202+
(*load_out)->is_done = (handle->origin_load.valid_size == expected_size) ? handle->origin_load.is_done : false;
203+
(*load_out)->pts = handle->origin_load.pts;
204+
handle->origin_load.valid_size -= expected_size;
205+
handle->origin_load.buf += expected_size;
206+
}
207+
}
208+
if (handle->buf_filled || (handle->origin_load.valid_size < expected_size)) {
209+
int n = (handle->buf_len - handle->buf_filled) > handle->origin_load.valid_size
210+
? handle->origin_load.valid_size
211+
: (handle->buf_len - handle->buf_filled);
212+
memcpy(handle->buf + handle->buf_filled, handle->origin_load.buf, n);
213+
handle->origin_load.buf += n;
214+
handle->origin_load.valid_size -= n;
215+
handle->buf_filled += n;
216+
ESP_LOGD("GMF_CACHE", "ACQ, filled: %ld, used size: %d, origin_left_size: %d", handle->buf_filled, n, handle->origin_load.valid_size);
217+
}
218+
if ((*load_out)->valid_size == 0) {
219+
(*load_out)->buf = handle->buf;
220+
(*load_out)->buf_length = handle->buf_len;
221+
(*load_out)->valid_size = handle->buf_filled;
222+
// FIXME If the data is split into several pieces, the pts remains the same.
223+
(*load_out)->pts = handle->origin_load.pts;
224+
(*load_out)->is_done = (handle->origin_load.valid_size == 0) ? handle->origin_load.is_done : false;
225+
}
226+
227+
return ESP_GMF_ERR_OK;
228+
}
229+
230+
/**
231+
* @brief Release the payload data previously acquired with `esp_gmf_cache_acquire`
232+
*
233+
* @note The filled portion of the buffer is cleared only if its size equals the buffer length
234+
*
235+
* @param[in] handle Pointer to the cache handle
236+
* @param[in] load Pointer to the payload data to be released
237+
*
238+
* @return
239+
* - ESP_GMF_ERR_OK Payload data successfully released
240+
* - ESP_GMF_ERR_INVALID_ARG If either `handle` or `load` is NULL
241+
*/
242+
static inline esp_gmf_err_t esp_gmf_cache_release(esp_gmf_cache_t *handle, esp_gmf_payload_t *load)
243+
{
244+
if ((handle == NULL) || (load == NULL)) {
245+
return ESP_GMF_ERR_INVALID_ARG;
246+
}
247+
ESP_LOGD("GMF_CACHE", "RLS, buf:%p-%p, filled: %ld, origin_valid_size: %d", load->buf, handle->buf, handle->buf_filled, handle->origin_load.valid_size);
248+
if ((load->buf == handle->buf) && (handle->buf_filled == handle->buf_len)) {
249+
handle->buf_filled = 0;
250+
}
251+
memset(&handle->load, 0, sizeof(esp_gmf_payload_t));
252+
return ESP_GMF_ERR_OK;
253+
}
254+
255+
/**
256+
* @brief Get the total amount of cached data
257+
*
258+
* @param[in] handle Pointer to the cache handle
259+
* @param[out] filled Pointer to store the filled data size
260+
*
261+
* @return
262+
* - ESP_GMF_ERR_OK Success, `filled` is set
263+
* - ESP_GMF_ERR_INVALID_ARG If `handle` or `filled` is NULL
264+
*/
265+
static inline esp_gmf_err_t esp_gmf_cache_get_cached_size(esp_gmf_cache_t *handle, int *filled)
266+
{
267+
if ((handle == NULL) || (filled == NULL)) {
268+
return ESP_GMF_ERR_INVALID_ARG;
269+
}
270+
*filled = handle->buf_filled + handle->origin_load.valid_size;
271+
return ESP_GMF_ERR_OK;
272+
}
273+
274+
#ifdef __cplusplus
275+
}
276+
#endif /* __cplusplus */

0 commit comments

Comments
 (0)