You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/02/02 14:12:05 UTC

[nuttx-apps] branch master updated: nxrecorder: add support to record media file.

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 61c4050d3 nxrecorder: add support to record media file.
61c4050d3 is described below

commit 61c4050d31b13816fa619fca54be713f38bea7bf
Author: qiaohaijiao1 <qi...@xiaomi.com>
AuthorDate: Wed Jan 18 13:49:25 2023 +0800

    nxrecorder: add support to record media file.
    
    Signed-off-by: qiaohaijiao1 <qi...@xiaomi.com>
---
 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
  *