Files
story/firmware/echoear-platform-agent/tests/capture-restart-host.cjs
T

135 lines
9.2 KiB
JavaScript
Raw Normal View History

// Compile actual capture lifecycle and recorder close/read functions with
// concurrent workers. No device, microphone or provider access.
const fs=require('node:fs'),path=require('node:path');
const root=path.resolve(__dirname,'..');
const capture=fs.readFileSync(path.join(root,'components/application/platform/src/espressif/volc_hal_capture.c'),'utf8').replace(/^#include .*$/gm,'');
const recorder=fs.readFileSync(path.join(root,'components/av_processor/audio_processor.c'),'utf8');
function section(first,last){const a=recorder.indexOf(first),b=recorder.indexOf(last,a);if(a<0||b<0)throw Error(first);return recorder.slice(a,b);}
process.stdout.write(String.raw`
#include <assert.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
typedef int esp_err_t;
#define ESP_OK 0
#define ESP_FAIL -1
#define ESP_LOGI(...) ((void)0)
#define ESP_LOGW(...) ((void)0)
#define ESP_LOGE(...) ((void)0)
#define LOGI(...) ((void)0)
#define LOGE(...) ((void)0)
#define pdMS_TO_TICKS(x) (x)
#define portMAX_DELAY 0
#define MALLOC_CAP_SPIRAM 1
#define MALLOC_CAP_8BIT 2
#define pdPASS 1
#define VOLC_MEDIA_TYPE_AUDIO 0
#define VOLC_MEDIA_TYPE_VIDEO 1
#define VOLC_HAL_CAPTURE_AUDIO 0
#define VOLC_AUDIO_DATA_TYPE_PCM 0
#define AV_PROCESSOR_FORMAT_ID_PCM 0
typedef int volc_media_type_e;
typedef enum {VOLC_AUDIO_MODE_WAKEUP,VOLC_AUDIO_MODE_CAPTURE} volc_hal_audio_capture_mode_e;
typedef void *volc_hal_capture_t;
typedef struct {int data_type;void *user_data;} volc_hal_frame_info_t;
typedef void (*volc_hal_capture_data_cb_t)(void*,const void*,int,volc_hal_frame_info_t*);
typedef void (*volc_hal_audio_wakeup_cb_t)(void*,void*);
typedef struct {int media_type;volc_hal_capture_data_cb_t data_cb;volc_hal_audio_wakeup_cb_t audio_wakeup_cb;void *user_data;} volc_hal_capture_config_t;
typedef struct {void *capture_handle[1];} volc_hal_context_t;
static volc_hal_context_t hal;
static volc_hal_context_t *volc_get_global_hal_context(void){return &hal;}
typedef struct {void *rec_dev;} basic_board_periph_t;
basic_board_periph_t global_periph;
static void esp_codec_dev_set_in_gain(void *d,float g){(void)d;(void)g;}
typedef struct {int format;struct {struct {struct {int sample_rate,sample_bits,channels,frame_duration;} audio_info;} g711;} params;} av_processor_encoder_config_t;
typedef struct {bool ai_mode_wakeup;} av_processor_afe_config_t;
typedef struct {av_processor_encoder_config_t encoder_cfg;av_processor_afe_config_t afe_config;volc_hal_audio_wakeup_cb_t recorder_event_cb;void *recorder_ctx;} audio_recorder_config_t;
#define DEFAULT_AV_PROCESSOR_AFE_CONFIG() {0}
#define DEFAULT_AUDIO_RECORDER_CONFIG() {0}
typedef unsigned UBaseType_t;
static UBaseType_t uxTaskPriorityGet(void *t){(void)t;return 5;}
static void vTaskPrioritySet(void *t,unsigned p){(void)t;(void)p;}
static void vTaskDelay(unsigned ticks){(void)ticks;struct timespec t={0,1000000};nanosleep(&t,NULL);}
typedef struct {pthread_mutex_t mutex;pthread_cond_t cond;int value;} *SemaphoreHandle_t;
static unsigned semaphores,tasks,allocations,pipelines;
static SemaphoreHandle_t xSemaphoreCreateBinary(void){
SemaphoreHandle_t s=calloc(1,sizeof(*s));assert(s);pthread_mutex_init(&s->mutex,NULL);pthread_cond_init(&s->cond,NULL);semaphores++;return s;
}
static int xSemaphoreTake(SemaphoreHandle_t s,unsigned timeout){(void)timeout;pthread_mutex_lock(&s->mutex);while(!s->value)pthread_cond_wait(&s->cond,&s->mutex);s->value--;pthread_mutex_unlock(&s->mutex);return 1;}
static void xSemaphoreGive(SemaphoreHandle_t s){pthread_mutex_lock(&s->mutex);s->value++;pthread_cond_signal(&s->cond);pthread_mutex_unlock(&s->mutex);}
static void vSemaphoreDelete(SemaphoreHandle_t s){pthread_mutex_destroy(&s->mutex);pthread_cond_destroy(&s->cond);free(s);semaphores--;}
typedef struct {pthread_t thread;void (*fn)(void*);void *arg;} *TaskHandle_t;
static bool task_fail,open_fail,buffer_fail;
static void *worker(void *p){TaskHandle_t t=p;t->fn(t->arg);assert(0);return NULL;}
static int xTaskCreatePinnedToCoreWithCaps(void (*fn)(void*),const char *name,unsigned stack,void *arg,unsigned priority,TaskHandle_t *h,int core,unsigned caps){
(void)name;(void)stack;(void)priority;(void)core;(void)caps;if(task_fail)return 0;
TaskHandle_t t=calloc(1,sizeof(*t));assert(t);t->fn=fn;t->arg=arg;*h=t;tasks++;assert(!pthread_create(&t->thread,NULL,worker,t));return pdPASS;
}
static void vTaskSuspend(void *h){assert(!h);pthread_exit(NULL);}
static void vTaskDeleteWithCaps(TaskHandle_t t){assert(!pthread_join(t->thread,NULL));free(t);tasks--;}
static void *volc_osal_malloc(size_t n){if(buffer_fail)return NULL;void *p=malloc(n);assert(p);allocations++;return p;}
static void *volc_osal_calloc(size_t n,size_t s){void *p=calloc(n,s);assert(p);allocations++;return p;}
static void volc_osal_free(void *p){if(p){free(p);allocations--;}}
enum {AUDIO_RUN_STATE_CLOSED,AUDIO_RUN_STATE_PLAYING};
typedef struct {int valid_size;void *buf;} esp_gmf_data_bus_block_t;
typedef struct {pthread_mutex_t mutex;pthread_cond_t cond;bool aborted,supply;int borrowers;char data[1920];} fifo_t;
static struct {int state;void *pipe,*task,*afe_cfg,*afe_manager,*pool;fifo_t *fifo;void *cb,*ctx;} audio_recorder;
static atomic_bool recorder_closing;
static atomic_uint recorder_readers,frames;
static SemaphoreHandle_t control;
static SemaphoreHandle_t recorder_control_lock(void){return control;}
static atomic_uint fifo_waiters;
static int esp_gmf_fifo_acquire_read(fifo_t *f,esp_gmf_data_bus_block_t *b,int n,unsigned timeout){
(void)timeout;pthread_mutex_lock(&f->mutex);atomic_fetch_add(&fifo_waiters,1);
while(!f->aborted&&!f->supply)pthread_cond_wait(&f->cond,&f->mutex);
atomic_fetch_sub(&fifo_waiters,1);
int ret=-1;if(!f->aborted){f->supply=false;f->borrowers++;b->valid_size=n;b->buf=f->data;ret=0;}
pthread_mutex_unlock(&f->mutex);return ret;
}
static void esp_gmf_fifo_release_read(fifo_t *f,esp_gmf_data_bus_block_t *b,unsigned t){(void)b;(void)t;pthread_mutex_lock(&f->mutex);assert(f->borrowers==1);f->borrowers--;pthread_mutex_unlock(&f->mutex);}
static void esp_gmf_fifo_abort(fifo_t *f){pthread_mutex_lock(&f->mutex);f->aborted=true;pthread_cond_broadcast(&f->cond);pthread_mutex_unlock(&f->mutex);}
static void esp_gmf_pipeline_stop(void *p){assert(p);}
static void esp_gmf_pipeline_destroy(void *p){assert(p);free(p);pipelines--;}
static void esp_gmf_task_deinit(void *p){assert(p);free(p);}
static void esp_gmf_fifo_destroy(fifo_t *f){assert(!atomic_load(&recorder_readers)&&!f->borrowers);pthread_mutex_destroy(&f->mutex);pthread_cond_destroy(&f->cond);free(f);}
static void afe_config_free(void *p){free(p);}
static void esp_gmf_afe_manager_destroy(void *p){free(p);}
static void av_audio_recorder_pool_deinit(void *p){free(p);}
`+section('static void recorder_cleanup_on_error(void)','static esp_err_t recorder_configure_all_encoders(void)')+section('static esp_err_t recorder_close_internal(void)','esp_err_t audio_feeder_feed_data(uint8_t')+String.raw`
static int audio_recorder_open(audio_recorder_config_t *c){
(void)c;assert(audio_recorder.state==AUDIO_RUN_STATE_CLOSED);
audio_recorder.pipe=malloc(1);pipelines++;audio_recorder.task=malloc(1);audio_recorder.afe_cfg=malloc(1);audio_recorder.afe_manager=malloc(1);audio_recorder.pool=malloc(1);
audio_recorder.fifo=calloc(1,sizeof(fifo_t));assert(audio_recorder.fifo);pthread_mutex_init(&audio_recorder.fifo->mutex,NULL);pthread_cond_init(&audio_recorder.fifo->cond,NULL);audio_recorder.fifo->supply=true;
if(open_fail){recorder_cleanup_on_error();assert(!audio_recorder.pool&&!audio_recorder.pipe&&!audio_recorder.task&&!audio_recorder.fifo&&!audio_recorder.afe_cfg&&!audio_recorder.afe_manager);return -1;}
audio_recorder.state=AUDIO_RUN_STATE_PLAYING;return 0;
}
`+capture+String.raw`
static void captured(void *c,const void *b,int n,volc_hal_frame_info_t *f){(void)c;(void)b;(void)n;(void)f;atomic_fetch_add(&frames,1);}
int main(void){
control=xSemaphoreCreateBinary();xSemaphoreGive(control);
volc_hal_capture_config_t cfg={.media_type=VOLC_MEDIA_TYPE_AUDIO,.data_cb=captured};
volc_hal_capture_t c=volc_hal_capture_create(&cfg);assert(c);
for(unsigned i=0;i<100;i++){
assert(!volc_hal_capture_start(c,i%2?VOLC_AUDIO_MODE_CAPTURE:VOLC_AUDIO_MODE_WAKEUP));
while(!atomic_load(&fifo_waiters))vTaskDelay(1);
assert(pipelines==1&&tasks==1);
assert(!volc_hal_capture_stop(c));assert(!pipelines&&!tasks&&!recorder_readers&&!fifo_waiters);
assert(!((volc_hal_capture_impl_t*)c)->capture_thread);
assert(!volc_hal_capture_update_config(c,&cfg));assert(!volc_hal_capture_stop(c));
}
task_fail=true;assert(volc_hal_capture_start(c,VOLC_AUDIO_MODE_CAPTURE)==-1);assert(!pipelines&&!tasks);task_fail=false;
open_fail=true;assert(volc_hal_capture_start(c,VOLC_AUDIO_MODE_CAPTURE)==-1);assert(!pipelines&&!tasks);open_fail=false;
buffer_fail=true;assert(volc_hal_capture_start(c,VOLC_AUDIO_MODE_CAPTURE)==-1);buffer_fail=false;
assert(!volc_hal_capture_start(c,VOLC_AUDIO_MODE_CAPTURE));while(!fifo_waiters)vTaskDelay(1);
volc_hal_capture_destroy(c);assert(!hal.capture_handle[0]&&!pipelines&&!tasks&&!allocations);
vSemaphoreDelete(control);assert(!semaphores&&frames>=50);
puts("PASS 100 concurrent capture/wakeup restarts: blocked reader abort and join, pipeline/WithCaps/handle/buffer cleanup, duplicate stop, open/buffer/task failure recovery, destroy");
return 0;
}
`);