# GMF-Misc

[中文]

gmf_misc collects utility-type elements in ESP-GMF that have a single function and do not belong to the three main categories of codec / IO / effects. Currently it contains only `copier`: a single-input multiple-output element that broadcasts one frame of payload from upstream to N downstream paths at once, implementing a fan-out topology in conjunction with the pipeline cascading mechanism. For the element base class, see GMF Elements; for the data path, see Data Flow; for pipeline composition, see GMF Pipeline and Task Scheduling.

## Feature List

- copier: broadcasts the payload from a single input port to multiple output ports; the first output shares the same data as the input (zero-copy), while other outputs each copy the data via `esp_gmf_payload_copy_data`

- Port type: both input and output ports support `ESP_GMF_PORT_TYPE_BYTE` and `ESP_GMF_PORT_TYPE_BLOCK`, automatically adapting to upstream and downstream

- Pass-through `is_done` flag: the end-of-stream marker in the input payload is passed to the first output, allowing downstream pipelines to terminate naturally

## Technical Details

### How copier Works

copier is a tool for implementing “fan-out” topology in a pipeline. On the `esp_gmf_element_t` base class, the input port attribute is set to `ESP_GMF_EL_PORT_CAP_SINGLE` (only 1 input), and the output port attribute is set to `ESP_GMF_EL_PORT_CAP_MULTI` (allows multiple connections). After registering to the pool, the application connects multiple pipelines / ports to copier’s output via `esp_gmf_pipeline_connect_pipe` or `esp_gmf_pipeline_reg_el_port`; at runtime, copier performs the following in each `process`:

1. `acquire_in` retrieves one frame of input payload; the length is determined by `data_length`.

2. Iterate over the output port list, for each output:

   - The first output (list head) is handled specially: `out_load` points to the same input payload, syncing only `valid_size` and `is_done`; on `release_out`, it is passed to downstream by reference count, avoiding memory copying;

   - Other outputs each `acquire_out` an output payload of capacity >= valid input bytes, call `esp_gmf_payload_copy_data` to copy the input data, then `release_out`.

3. After all outputs are released, `release_in` returns the input payload.

Each output’s `release_out` uses its respective port’s `wait_ticks`; the application can set different blocking times for different downstream elements when adding ports, avoiding the entire copier from blocking when one downstream is slow. Regarding return values: when the input payload’s `is_done` is triggered, `ESP_GMF_JOB_ERR_DONE` is returned directly, causing the task to remove copier from the job list; under normal conditions, it returns the current frame’s `valid_size`.

### Configuration

`esp_gmf_copier_cfg_t` has only one field:

    typedef struct {
        uint8_t copy_num;  /* number of copies */
    } esp_gmf_copier_cfg_t;

`copy_num` declares the number of planned output connections at initialization. The actual number of connections to copier is determined by the number of output ports bound when the pipeline is created; `copy_num` is primarily used for resource estimation and logging.

### Code Example

After registering copier to the pool, it can appear in the pipeline string by name `"copier"`:

    esp_gmf_copier_cfg_t cfg = { .copy_num = 2 };
    esp_gmf_obj_handle_t copier = NULL;
    esp_gmf_copier_init(&cfg, &copier);
    esp_gmf_pool_register_element(pool, copier, NULL);

    /* Main pipeline: File → Decoder → Copier → Resample → I2S */
    const char *p1_els[] = {"aud_dec", "copier", "aud_rate_cvt"};
    esp_gmf_pipeline_handle_t p1 = NULL;
    esp_gmf_pool_new_pipeline(pool, "io_file", p1_els, 3, "io_codec_dev", &p1);

    /* Side pipeline: Second output of Copier → Resample → File */
    const char *p2_els[] = {"aud_rate_cvt"};
    esp_gmf_pipeline_handle_t p2 = NULL;
    esp_gmf_pool_new_pipeline(pool, NULL, p2_els, 1, "io_file", &p2);

    /* Connect the output port of copier in p1 to the resample input port of p2 */
    esp_gmf_pipeline_connect_pipe(p1, "copier", NULL, p2, "aud_rate_cvt", NULL);

For the complete “fan-out” topology combination (event cascading, prev_run coordination), see GMF Pipeline and Task Scheduling.

## Application Examples

- `elements/test_apps/main/elements/gmf_audio_play_el_test.c`: Comprehensive use case demonstrating copier combined with audio elements

- GMF-Core Overview “fan-out splitting” typical topology: simultaneously sends the same audio to the AEC reference path while playing music

## FAQ

Q: What is the difference between copier’s first output and the others?

The first output shares the same payload as the input (zero-copy); the other outputs each allocate and copy a separate payload via `esp_gmf_payload_copy_data` in `process`. Connect the downstream with the highest latency and throughput requirements to the first output to avoid one memcpy.

Q: Is there a limit on how many outputs copier can have?

There is no fixed upper limit at the source code level; `copy_num` is only used for resource estimation. The actual limit depends on memory: each additional output adds one payload buffer. Plan according to downstream element throughput and buffer together.

Q: Will one slow downstream affect the others?

Yes. copier serially calls `release_out` in list order during `process`; when one output blocks, the others also wait. Place latency-sensitive downstream elements first (first output), and set shorter `wait_ticks` for slower ports; when the timeout is reached, copier skips that release and logs a warning.

Q: Can copier be placed at the end of a pipeline?

Yes, but when placed at the end it cannot split data to subsequent elements. The common usage is to place copier in the middle, with some output ports connected to subsequent elements in the current pipeline and others connected to a branch pipeline via `esp_gmf_pipeline_connect_pipe`.

## API Reference

Header files for this component:

- `esp_gmf_copier.h`: copier configuration and initialization

### Header File

- elements/gmf_misc/include/esp_gmf_copier.h

### Functions

esp_gmf_err_t esp_gmf_copier_init(esp_gmf_copier_cfg_t *config, esp_gmf_obj_handle_t *handle)

  Initializes the GMF copier using copier configuration.

  Parameters:

    - config – [in] Pointer to the copier configuration

    - handle – [out] Pointer to the copier handle to be initialized

  Returns:

    - ESP_GMF_ERR_OK Success

    - ESP_GMF_ERR_INVALID_ARG Invalid configuration provided

    - ESP_GMF_ERR_MEMORY_LACK Failed to allocate memory

### Structures

struct esp_gmf_copier_cfg_t

  Configuration for the GMF copier.

  Public Members

  uint8_t copy_num

    Number of copies

Was this page helpful?

- Thank you! We received your feedback.
- If you have any comments, fill in Espressif Documentation Feedback Form.

- We value your feedback.
- Let us know how we can improve this page by filling in Espressif Documentation Feedback Form.