From 61c4050d31b13816fa619fca54be713f38bea7bf Mon Sep 17 00:00:00 2001 From: qiaohaijiao1 Date: Wed, 18 Jan 2023 13:49:25 +0800 Subject: [PATCH] nxrecorder: add support to record media file. Signed-off-by: qiaohaijiao1 --- include/system/nxrecorder.h | 9 +- system/nxrecorder/Kconfig | 10 + system/nxrecorder/nxrecorder.c | 276 +++++++++++++++++++++++++--- system/nxrecorder/nxrecorder_main.c | 91 ++++++++- 4 files changed, 353 insertions(+), 33 deletions(-) diff --git a/include/system/nxrecorder.h b/include/system/nxrecorder.h index 129a23170..a06d86df4 100644 --- a/include/system/nxrecorder.h +++ b/include/system/nxrecorder.h @@ -153,7 +153,7 @@ int nxrecorder_setdevice(FAR struct nxrecorder_s *precorder, FAR const char *device); /**************************************************************************** - * Name: nxrecorder_recordraw + * Name: nxrecorder_recordinternal * * Plays the specified media file (from the filesystem) using the * Audio system. If a preferred device has been set, that device @@ -173,9 +173,10 @@ int nxrecorder_setdevice(FAR struct nxrecorder_s *precorder, * ****************************************************************************/ -int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, - FAR const char *filename, uint8_t nchannels, - uint8_t bpsamp, uint32_t samprate, uint8_t chmap); +int nxrecorder_recordinternal(FAR struct nxrecorder_s *precorder, + FAR const char *filename, int filefmt, + uint8_t nchannels, uint8_t bpsamp, + uint32_t samprate, uint8_t chmap); /**************************************************************************** * Name: nxrecorder_stop diff --git a/system/nxrecorder/Kconfig b/system/nxrecorder/Kconfig index 4dee937c4..dd274eb2e 100644 --- a/system/nxrecorder/Kconfig +++ b/system/nxrecorder/Kconfig @@ -43,5 +43,15 @@ config NXRECORDER_INCLUDE_HELP Compiles in the NxRecorder help text to provide online help for available commands with syntax. +config NXRECORDER_FMT_FROM_EXT + bool "Include code to determine Audio format from extension" + default y + ---help--- + Compiles in extra code to determine audio format based + on the filename extension for known file types. + This feature is used if the format is not manually + specified, and will take priority over the more lengthy + file content detection approach. + endif endif diff --git a/system/nxrecorder/nxrecorder.c b/system/nxrecorder/nxrecorder.c index 9b6cb4372..cc6f3fa20 100644 --- a/system/nxrecorder/nxrecorder.c +++ b/system/nxrecorder/nxrecorder.c @@ -58,6 +58,62 @@ # define CONFIG_NXRECORDER_RECORDTHREAD_STACKSIZE 1500 #endif +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +#ifdef CONFIG_NXRECORDER_FMT_FROM_EXT +struct nxrecorder_ext_fmt_s +{ + FAR const char *ext; + uint16_t format; + CODE int (*getsubformat)(int fd); +}; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_FORMAT_MP3 +int nxrecorder_getmp3subformat(int fd); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_NXRECORDER_FMT_FROM_EXT +static const struct nxrecorder_ext_fmt_s g_known_ext[] = +{ +#ifdef CONFIG_AUDIO_FORMAT_AC3 + { "ac3", AUDIO_FMT_AC3, NULL }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_MP3 + { "mp3", AUDIO_FMT_MP3, nxrecorder_getmp3subformat }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_DTS + { "dts", AUDIO_FMT_DTS, NULL }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_WMA + { "wma", AUDIO_FMT_WMA, NULL }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_PCM + { "wav", AUDIO_FMT_PCM, NULL }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_MIDI + { "mid", AUDIO_FMT_MIDI, NULL }, + { "midi", AUDIO_FMT_MIDI, NULL }, +#endif +#ifdef CONFIG_AUDIO_FORMAT_OGG_VORBIS + { "ogg", AUDIO_FMT_OGG_VORBIS, NULL } +#endif +}; + +static const int g_known_ext_count = sizeof(g_known_ext) / + sizeof(struct nxrecorder_ext_fmt_s); +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -77,8 +133,13 @@ * ****************************************************************************/ -static int nxrecorder_opendevice(FAR struct nxrecorder_s *precorder) +static int nxrecorder_opendevice(FAR struct nxrecorder_s *precorder, + int format, int subfmt) { + struct audio_caps_s cap; + bool supported = true; + int x; + /* If we have a device, then open it */ if (precorder->device[0] != '\0') @@ -101,7 +162,79 @@ static int nxrecorder_opendevice(FAR struct nxrecorder_s *precorder) return -ENOENT; } - return OK; + cap.ac_len = sizeof(cap); + cap.ac_type = AUDIO_TYPE_QUERY; + cap.ac_subtype = AUDIO_TYPE_QUERY; + + if (ioctl(precorder->dev_fd, AUDIOIOC_GETCAPS, + (uintptr_t)&cap) == cap.ac_len) + { + if (((cap.ac_format.hw & (1 << (format - 1))) != 0) && + (cap.ac_controls.b[0] & AUDIO_TYPE_INPUT)) + { + /* Test if subformat needed and detected */ + + if (subfmt != AUDIO_FMT_UNDEF) + { + /* Prepare to get sub-formats for + * this main format + */ + + cap.ac_subtype = format; + cap.ac_format.b[0] = 0; + + while (ioctl(precorder->dev_fd, AUDIOIOC_GETCAPS, + (uintptr_t)&cap) == cap.ac_len) + { + /* Check the next set of 4 controls + * to find the subformat + */ + + for (x = 0; x < sizeof(cap.ac_controls.b); x++) + { + if (cap.ac_controls.b[x] == subfmt) + { + /* Sub format supported! */ + + break; + } + else if (cap.ac_controls.b[x] == + AUDIO_SUBFMT_END) + { + /* Sub format not supported */ + + supported = false; + break; + } + } + + /* If we reached the end of the subformat list, + * then break out of the loop. + */ + + if (x != sizeof(cap.ac_controls)) + { + break; + } + + /* Increment ac_format.b[0] to get next + * set of subformats + */ + + cap.ac_format.b[0]++; + } + } + + if (supported) + { + /* Yes, it supports this format. Use this device */ + + return OK; + } + } + + close(precorder->dev_fd); + } } /* Device not found */ @@ -111,6 +244,84 @@ static int nxrecorder_opendevice(FAR struct nxrecorder_s *precorder) return -ENODEV; } +/**************************************************************************** + * Name: nxrecorder_getmp3subformat + * + * nxrecorder_getmp3subformat() just ruturn AUDIO_SUBFMT_PCM_MP3. + * + ****************************************************************************/ + +#ifdef CONFIG_AUDIO_FORMAT_MP3 +int nxrecorder_getmp3subformat(int fd) +{ + return AUDIO_SUBFMT_PCM_MP3; +} +#endif + +#ifdef CONFIG_NXRECORDER_FMT_FROM_EXT + +/**************************************************************************** + * Name: nxprecorder_fmtfromext + * + * nxrecorder_fmtfromext() tries to determine the file format based + * on the extension of the supplied filename. + * + ****************************************************************************/ + +static inline int nxrecorder_fmtfromext(FAR struct nxrecorder_s *precorder, + FAR const char *pfilename, + FAR int *subfmt) +{ + FAR const char *pext; + uint8_t x; + uint8_t c; + + /* Find the file extension, if any */ + + x = strlen(pfilename) - 1; + while (x > 0) + { + /* Search backward for the first '.' */ + + if (pfilename[x] == '.') + { + /* First '.' found. Now compare with known extensions */ + + pext = &pfilename[x + 1]; + for (c = 0; c < g_known_ext_count; c++) + { + /* Test for extension match */ + + if (strcasecmp(pext, g_known_ext[c].ext) == 0) + { + /* Test if we have a sub-format detection routine */ + + if (subfmt && g_known_ext[c].getsubformat) + { + *subfmt = g_known_ext[c].getsubformat(precorder->fd); + } + + /* Return the format for this extension */ + + return g_known_ext[c].format; + } + } + } + + /* Stop if we find a '/' */ + + if (pfilename[x] == '/') + { + break; + } + + x--; + } + + return AUDIO_FMT_UNDEF; +} +#endif + /**************************************************************************** * Name: nxrecorder_writebuffer * @@ -198,7 +409,7 @@ static int nxrecorder_enqueuebuffer(FAR struct nxrecorder_s *precorder, bufdesc.u.buffer = apb; ret = ioctl(precorder->dev_fd, AUDIOIOC_ENQUEUEBUFFER, - (unsigned long)&bufdesc); + (uintptr_t)&bufdesc); if (ret < 0) { int errcode = errno; @@ -276,7 +487,7 @@ static FAR void *nxrecorder_recordthread(pthread_addr_t pvarg) /* Query the audio device for its preferred buffer size / qty */ if ((ret = ioctl(precorder->dev_fd, AUDIOIOC_GETBUFFERINFO, - (unsigned long) &buf_info)) != OK) + (uintptr_t)&buf_info)) != OK) { /* Driver doesn't report its buffer size. Use our default. */ @@ -316,7 +527,7 @@ static FAR void *nxrecorder_recordthread(pthread_addr_t pvarg) buf_desc.u.pbuffer = &pbuffers[x]; ret = ioctl(precorder->dev_fd, AUDIOIOC_ALLOCBUFFER, - (unsigned long) &buf_desc); + (uintptr_t)&buf_desc); if (ret != sizeof(buf_desc)) { /* Buffer alloc Operation not supported or error allocating! */ @@ -378,7 +589,7 @@ static FAR void *nxrecorder_recordthread(pthread_addr_t pvarg) { #ifdef CONFIG_AUDIO_MULTI_SESSION ret = ioctl(precorder->dev_fd, AUDIOIOC_START, - (unsigned long) precorder->session); + (uintptr_t)precorder->session); #else ret = ioctl(precorder->dev_fd, AUDIOIOC_START, 0); #endif @@ -522,7 +733,7 @@ static FAR void *nxrecorder_recordthread(pthread_addr_t pvarg) #ifdef CONFIG_AUDIO_MULTI_SESSION ioctl(precorder->dev_fd, AUDIOIOC_STOP, - (unsigned long) precorder->session); + (uintptr_t)precorder->session); #else ioctl(precorder->dev_fd, AUDIOIOC_STOP, 0); #endif @@ -570,7 +781,7 @@ err_out: buf_desc.u.buffer = pbuffers[x]; ioctl(precorder->dev_fd, AUDIOIOC_FREEBUFFER, - (unsigned long) &buf_desc); + (uintptr_t)&buf_desc); } } @@ -583,11 +794,11 @@ err_out: ioctl(precorder->dev_fd, AUDIOIOC_UNREGISTERMQ, - (unsigned long) precorder->mq); + (uintptr_t)precorder->mq); #ifdef CONFIG_AUDIO_MULTI_SESSION ioctl(precorder->dev_fd, AUDIOIOC_RELEASE, - (unsigned long) precorder->session); + (uintptr_t)precorder->session); #else ioctl(precorder->dev_fd, AUDIOIOC_RELEASE, @@ -646,7 +857,7 @@ int nxrecorder_pause(FAR struct nxrecorder_s *precorder) { #ifdef CONFIG_AUDIO_MULTI_SESSION ret = ioctl(precorder->dev_fd, AUDIOIOC_PAUSE, - (unsigned long) precorder->session); + (uintptr_t)precorder->session); #else ret = ioctl(precorder->dev_fd, AUDIOIOC_PAUSE, 0); #endif @@ -676,7 +887,7 @@ int nxrecorder_resume(FAR struct nxrecorder_s *precorder) { #ifdef CONFIG_AUDIO_MULTI_SESSION ret = ioctl(precorder->dev_fd, AUDIOIOC_RESUME, - (unsigned long) precorder->session); + (uintptr_t)precorder->session); #else ret = ioctl(precorder->dev_fd, AUDIOIOC_RESUME, 0); #endif @@ -770,10 +981,10 @@ int nxrecorder_stop(FAR struct nxrecorder_s *precorder) #endif /* CONFIG_AUDIO_EXCLUDE_STOP */ /**************************************************************************** - * Name: nxrecorder_recordraw + * Name: nxrecorder_recordinteral * - * nxrecorder_recordraw() tries to record the raw data file using the Audio - * system. If a device is specified, it will try to use that + * nxrecorder_recordinternal() tries to record audio file using the Audio + * system. If a device is specified, it will try to use that * device. * Input: * precorder Pointer to the initialized MRecorder context @@ -792,9 +1003,10 @@ int nxrecorder_stop(FAR struct nxrecorder_s *precorder) * ****************************************************************************/ -int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, - FAR const char *pfilename, uint8_t nchannels, - uint8_t bpsamp, uint32_t samprate, uint8_t chmap) +int nxrecorder_recordinternal(FAR struct nxrecorder_s *precorder, + FAR const char *pfilename, int filefmt, + uint8_t nchannels, uint8_t bpsamp, + uint32_t samprate, uint8_t chmap) { struct mq_attr attr; struct sched_param sparam; @@ -802,6 +1014,7 @@ int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, struct audio_caps_desc_s cap_desc; struct ap_buffer_info_s buf_info; int ret; + int subfmt = AUDIO_FMT_UNDEF; DEBUGASSERT(precorder != NULL); DEBUGASSERT(pfilename != NULL); @@ -825,9 +1038,25 @@ int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, return -ENOENT; } + if (filefmt == AUDIO_FMT_UNDEF) + { + filefmt = nxrecorder_fmtfromext(precorder, pfilename, &subfmt); + } + + /* Test if we determined the file format */ + + if (filefmt == AUDIO_FMT_UNDEF) + { + /* Hmmm, it's some unknown / unsupported type */ + + auderr("ERROR: Unsupported format: %d\n", filefmt); + ret = -ENOSYS; + goto err_out_nodev; + } + /* Try to open the device */ - ret = nxrecorder_opendevice(precorder); + ret = nxrecorder_opendevice(precorder, filefmt, subfmt); if (ret < 0) { /* Error opening the device */ @@ -840,7 +1069,7 @@ int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, #ifdef CONFIG_AUDIO_MULTI_SESSION ret = ioctl(precorder->dev_fd, AUDIOIOC_RESERVE, - (unsigned long)&precorder->session); + (uintptr_t)&precorder->session); #else ret = ioctl(precorder->dev_fd, AUDIOIOC_RESERVE, 0); #endif @@ -863,8 +1092,9 @@ int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, cap_desc.caps.ac_controls.hw[0] = samprate ? samprate : 48000; cap_desc.caps.ac_controls.b[3] = samprate >> 16; cap_desc.caps.ac_controls.b[2] = bpsamp ? bpsamp : 16; + cap_desc.caps.ac_subtype = filefmt; ret = ioctl(precorder->dev_fd, AUDIOIOC_CONFIGURE, - (unsigned long)&cap_desc); + (uintptr_t)&cap_desc); if (ret < 0) { ret = -errno; @@ -874,7 +1104,7 @@ int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, /* Query the audio device for its preferred buffer count */ if (ioctl(precorder->dev_fd, AUDIOIOC_GETBUFFERINFO, - (unsigned long)&buf_info) != OK) + (uintptr_t)&buf_info) != OK) { /* Driver doesn't report its buffer size. Use our default. */ @@ -905,7 +1135,7 @@ int nxrecorder_recordraw(FAR struct nxrecorder_s *precorder, ioctl(precorder->dev_fd, AUDIOIOC_REGISTERMQ, - (unsigned long)precorder->mq); + (uintptr_t)precorder->mq); /* Check if there was a previous thread and join it if there was * to perform clean-up. diff --git a/system/nxrecorder/nxrecorder_main.c b/system/nxrecorder/nxrecorder_main.c index b5eb5fd37..c1c21fd50 100644 --- a/system/nxrecorder/nxrecorder_main.c +++ b/system/nxrecorder/nxrecorder_main.c @@ -68,6 +68,8 @@ static int nxrecorder_cmd_quit(FAR struct nxrecorder_s *precorder, FAR char *parg); static int nxrecorder_cmd_recordraw(FAR struct nxrecorder_s *precorder, FAR char *parg); +static int nxrecorder_cmd_record(FAR struct nxrecorder_s *precorder, + FAR char *parg); static int nxrecorder_cmd_device(FAR struct nxrecorder_s *precorder, FAR char *parg); @@ -120,6 +122,13 @@ static const struct mp_cmd_s g_nxrecorder_cmds[] = nxrecorder_cmd_recordraw, NXRECORDER_HELP_TEXT("Record a pcm raw file") }, + { + "record", + "filename", + nxrecorder_cmd_record, + NXRECORDER_HELP_TEXT("Record a media file") + }, + #ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME { "pause", @@ -186,12 +195,13 @@ static int nxrecorder_cmd_recordraw(FAR struct nxrecorder_s *precorder, /* Try to record the file specified */ - ret = nxrecorder_recordraw(precorder, - filename, - channels, - bpsamp, - samprate, - chmap); + ret = nxrecorder_recordinternal(precorder, + filename, + AUDIO_FMT_PCM, + channels, + bpsamp, + samprate, + chmap); /* nxrecorder_recordfile returned values: * @@ -231,6 +241,75 @@ static int nxrecorder_cmd_recordraw(FAR struct nxrecorder_s *precorder, return ret; } +/**************************************************************************** + * Name: nxrecorder_cmd_record + * + * nxrecorder_cmd_record() record the specified media file using the + * nxrecorder context. + * + ****************************************************************************/ + +static int nxrecorder_cmd_record(FAR struct nxrecorder_s *precorder, + FAR char *parg) +{ + int ret; + int channels = 0; + int bpsamp = 0; + int samprate = 0; + int chmap = 0; + char filename[128]; + + sscanf(parg, "%s %d %d %d %d", filename, &channels, &bpsamp, + &samprate, &chmap); + + /* Try to record the file specified */ + + ret = nxrecorder_recordinternal(precorder, + filename, + AUDIO_FMT_UNDEF, + channels, + bpsamp, + samprate, + chmap); + + /* nxrecorder_recordfile returned values: + * + * OK File is being recorded + * -EBUSY The media device is busy + * -ENOSYS The media file is an unsupported type + * -ENODEV No audio device suitable to play the media type + * -ENOENT The media file was not found + */ + + switch (-ret) + { + case OK: + break; + + case ENODEV: + printf("No suitable Audio Device found\n"); + break; + + case EBUSY: + printf("Audio device busy\n"); + break; + + case ENOENT: + printf("File %s not found\n", parg); + break; + + case ENOSYS: + printf("Unknown audio format\n"); + break; + + default: + printf("Error playing file: %d\n", -ret); + break; + } + + return ret; +} + /**************************************************************************** * Name: nxrecorder_cmd_stop *