nxrecorder: add support to record media file.

Signed-off-by: qiaohaijiao1 <qiaohaijiao1@xiaomi.com>
This commit is contained in:
qiaohaijiao1 2023-01-18 13:49:25 +08:00 committed by Xiang Xiao
parent 8cfcd274b2
commit 61c4050d31
4 changed files with 353 additions and 33 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
*