summaryrefslogtreecommitdiff
path: root/apps/system
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-08-05 10:04:24 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-08-05 10:04:24 -0600
commite54ef60df4e062df1f8fc4bd12792ee3f5dad057 (patch)
tree9afb51a4d69b1d14961be31ffe8b36f4d114c80e /apps/system
parent22156db1c97cec395d0302712e4590642d3e6a55 (diff)
downloadnuttx-e54ef60df4e062df1f8fc4bd12792ee3f5dad057.tar.gz
nuttx-e54ef60df4e062df1f8fc4bd12792ee3f5dad057.tar.bz2
nuttx-e54ef60df4e062df1f8fc4bd12792ee3f5dad057.zip
Break reading and enqueueing of audio buffers into two steps so that errors in enqueueing can be distinguished for errors in reading. Errors in enqueueing signal a downstream decoder error. Add logic to gracefully recover from downstream decoder errors.
Diffstat (limited to 'apps/system')
-rw-r--r--apps/system/nxplayer/nxplayer.c238
1 files changed, 189 insertions, 49 deletions
diff --git a/apps/system/nxplayer/nxplayer.c b/apps/system/nxplayer/nxplayer.c
index 53860f00d..fbc60b666 100644
--- a/apps/system/nxplayer/nxplayer.c
+++ b/apps/system/nxplayer/nxplayer.c
@@ -49,6 +49,8 @@
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -481,19 +483,16 @@ static int nxplayer_mediasearch(FAR struct nxplayer_s *pPlayer,
#endif
/****************************************************************************
- * Name: nxplayer_enqueuebuffer
+ * Name: nxplayer_readbuffer
*
- * Reads the next block of data from the media file into the specified
- * buffer and enqueues it to the audio device.
+ * Read the next block of data from the media file into the specified
+ * buffer.
*
****************************************************************************/
-static int nxplayer_enqueuebuffer(FAR struct nxplayer_s *pPlayer,
- FAR struct ap_buffer_s* apb)
+static int nxplayer_readbuffer(FAR struct nxplayer_s *pPlayer,
+ FAR struct ap_buffer_s* apb)
{
- struct audio_buf_desc_s bufdesc;
- int ret;
-
/* Validate the file is still open. It will be closed automatically when
* we encounter the end of file (or, perhaps, a read error that we cannot
* handle.
@@ -542,23 +541,43 @@ static int nxplayer_enqueuebuffer(FAR struct nxplayer_s *pPlayer,
{
DEBUGASSERT(errcode > 0);
auddbg("ERROR: fread failed: %d\n", errcode);
- UNUSED(errcode);
}
#endif
}
- /* We get here either on a successful read or a read error.
- *
- * We will have a zero length buffer (with the AUDIO_APB_FINAL set) if a
- * read error occurs or in the event that the file was an exact multiple
- * of the nmaxbytes size of the audio buffer. In that latter case, we
- * have an end of file with no bytes read.
- *
- * These infrequency zero length buffers have to be passed through because
- * the include the AUDIO_APB_FINAL flag that is needed to terminate the
- * audio stream.
+ /* Return OK to indicate that the buffer should be passed through to the
+ * audio device. This does not necessarily indicate that data was read
+ * correctly.
*/
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nxplayer_enqueuebuffer
+ *
+ * Description:
+ * Enqueue the audio buffer in the downstream device. Normally we are
+ * called with a buffer of data to be enqueued in the audio stream.
+ *
+ * Be we may also receive an empty length buffer (with only the
+ * AUDIO_APB_FINAL set) in the event of certin read error occurs or in the
+ * event that the file was an exact multiple of the nmaxbytes size of the
+ * audio buffer. In that latter case, we have an end of file with no bytes
+ * read.
+ *
+ * These infrequent zero length buffers have to be passed through because
+ * the include the AUDIO_APB_FINAL flag that is needed to terminate the
+ * audio stream.
+ *
+ ****************************************************************************/
+
+static int nxplayer_enqueuebuffer(FAR struct nxplayer_s *pPlayer,
+ FAR struct ap_buffer_s* apb)
+{
+ struct audio_buf_desc_s bufdesc;
+ int ret;
+
/* Now enqueue the buffer with the audio device. If the number of
* bytes in the file happens to be an exact multiple of the audio
* buffer size, then we will receive the last buffer size = 0. We
@@ -604,14 +623,18 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
struct audio_msg_s msg;
struct audio_buf_desc_s buf_desc;
ssize_t size;
- uint8_t running = true;
- uint8_t streaming = true;
+ bool running = true;
+ bool streaming = true;
+ bool failed = false;
#ifdef CONFIG_AUDIO_DRIVER_SPECIFIC_BUFFERS
struct ap_buffer_info_s buf_info;
FAR struct ap_buffer_s** pBuffers;
#else
FAR struct ap_buffer_s* pBuffers[CONFIG_AUDIO_NUM_BUFFERS];
#endif
+#ifdef CONFIG_DEBUG
+ int outstanding = 0;
+#endif
int prio;
int x;
int ret;
@@ -692,25 +715,71 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
for (x = 0; x < CONFIG_AUDIO_NUM_BUFFERS; x++)
#endif
{
- /* Enqueue next buffer */
+ /* Read the next buffer of data */
- ret = nxplayer_enqueuebuffer(pPlayer, pBuffers[x]);
+ ret = nxplayer_readbuffer(pPlayer, pBuffers[x]);
if (ret != OK)
{
- /* Error encoding initial buffers or file is small */
+ /* nxplayer_readbuffer will return an error if there is no further
+ * data to be read from the file. This can happen normally if the
+ * file is very small (less than will fit in
+ * CONFIG_AUDIO_NUM_BUFFERS) or if an error occurs trying to read
+ * from the file.
+ */
+
+ /* We are no longer streaming data from the file */
+
+ streaming = false;
if (x == 0)
{
+ /* No buffers read? Should never really happen. Even in the
+ * case of a read failure, one empty buffer containing the
+ * AUDIO_APB_FINAL indication will be returned.
+ */
+
running = false;
}
- else
+ }
+
+ /* Enqueue buffer by sending it to the audio driver */
+
+ else
+ {
+ ret = nxplayer_enqueuebuffer(pPlayer, pBuffers[x]);
+ if (ret != OK)
{
- /* We are no longer streaming data from the file */
+ /* Failed to enqueue the buffer. The driver is not happy with
+ * the buffer. Perhaps a decoder has detected something that it
+ * does not like in the stream and has stopped streaming. This
+ * would happen normally if we send a file in the incorrect format
+ * to an audio decoder.
+ *
+ * We must stop streaming as gracefully as possible. Close the
+ * file so that no further data is read.
+ */
+
+ fclose(pPlayer->fileFd);
+ pPlayer->fileFd = NULL;
- streaming = false;
+ /* We are no longer streaming data from the file. Be we will
+ * need to wait for any outstanding buffers to be recovered. We
+ * also still expect the audio driver to send a AUDIO_MSG_COMPLETE
+ * message after all queued buffers have been returned.
+ */
+
+ streaming = false;
+ failed = true;
+ break;
}
+#ifdef CONFIG_DEBUG
+ else
+ {
+ /* The audio driver has one more buffer */
- break;
+ outstanding++;
+ }
+#endif
}
}
@@ -719,46 +788,65 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
/* Start the audio device */
+ if (running && !failed)
+ {
#ifdef CONFIG_AUDIO_MULTI_SESSION
- ret = ioctl(pPlayer->devFd, AUDIOIOC_START,
- (unsigned long) pPlayer->session);
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_START,
+ (unsigned long) pPlayer->session);
#else
- ret = ioctl(pPlayer->devFd, AUDIOIOC_START, 0);
+ ret = ioctl(pPlayer->devFd, AUDIOIOC_START, 0);
#endif
- if (ret < 0)
- {
- /* Error starting the audio stream! */
+ if (ret < 0)
+ {
+ /* Error starting the audio stream! We need to continue running
+ * in order to recover the audio buffers that have already been
+ * queued.
+ */
- running = false;
+ failed = true;
+ }
}
- /* Indicate we are playing a file */
+ if (running && !failed)
+ {
+ /* Indicate we are playing a file */
- pPlayer->state = NXPLAYER_STATE_PLAYING;
+ pPlayer->state = NXPLAYER_STATE_PLAYING;
- /* Set initial parameters such as volume, bass, etc.
- * REVISIT: Shouldn't this actually be done BEFORE we start playing?
- */
+ /* Set initial parameters such as volume, bass, etc.
+ * REVISIT: Shouldn't this actually be done BEFORE we start playing?
+ */
#ifndef CONFIG_AUDIO_EXCLUDE_VOLUME
- (void)nxplayer_setvolume(pPlayer, pPlayer->volume);
+ (void)nxplayer_setvolume(pPlayer, pPlayer->volume);
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_BALANCE
- nxplayer_setbalance(pPlayer, pPlayer->balance);
+ nxplayer_setbalance(pPlayer, pPlayer->balance);
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_TONE
- nxplayer_setbass(pPlayer, pPlayer->bass);
- nxplayer_settreble(pPlayer, pPlayer->treble);
+ nxplayer_setbass(pPlayer, pPlayer->bass);
+ nxplayer_settreble(pPlayer, pPlayer->treble);
#endif
+ }
/* Loop until we specifically break. running == true means that we are
* still looping waiting for the playback to complete. All of the file
* data may have been sent (if streaming == false), but the playback is
* not complete until we get the AUDIO_MSG_COMPLETE (or AUDIO_MSG_STOP)
* message
+ *
+ * The normal protocol for streaming errors detected by the audio driver
+ * is as follows:
+ *
+ * (1) The audio driver will indicated the error by returning a negated
+ * error value when the next buffer is enqueued. The upper level
+ * then knows that this buffer was not queue.
+ * (2) The audio driver must return all queued buffers using the
+ * AUDIO_MSG_DEQUEUE message, and
+ * (3) Terminate playing by sending the AUDIO_MSG_COMPLETE message.
*/
audvdbg("%s\n", running ? "Playing..." : "Not runnning");
@@ -787,6 +875,15 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
/* An audio buffer is being dequeued by the driver */
case AUDIO_MSG_DEQUEUE:
+#ifdef CONFIG_DEBUG
+ /* Make sure that we believe that the audio driver has at
+ * least one buffer.
+ */
+
+ DEBUGASSERT(msg.u.pPtr && outstanding > 0);
+ outstanding--;
+#endif
+
/* Read data from the file directly into this buffer and
* re-enqueue it. streaming == true means that we have
* not yet hit the end-of-file.
@@ -794,7 +891,9 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
if (streaming)
{
- ret = nxplayer_enqueuebuffer(pPlayer, msg.u.pPtr);
+ /* Read the next buffer of data */
+
+ ret = nxplayer_readbuffer(pPlayer, msg.u.pPtr);
if (ret != OK)
{
/* Out of data. Stay in the loop until the device sends
@@ -804,6 +903,41 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
streaming = false;
}
+
+ /* Enqueue buffer by sending it to the audio driver */
+
+ else
+ {
+ ret = nxplayer_enqueuebuffer(pPlayer, msg.u.pPtr);
+ if (ret != OK)
+ {
+ /* There is some issue from the audio driver.
+ * Perhaps a problem in the file format?
+ *
+ * We must stop streaming as gracefully as possible.
+ * Close the file so that no further data is read.
+ */
+
+ fclose(pPlayer->fileFd);
+ pPlayer->fileFd = NULL;
+
+ /* Stop streaming and wait for buffers to be
+ * returned and to receive the AUDIO_MSG_COMPLETE
+ * indication.
+ */
+
+ streaming = false;
+ failed = true;
+ }
+#ifdef CONFIG_DEBUG
+ else
+ {
+ /* The audio driver has one more buffer */
+
+ outstanding++;
+ }
+#endif
+ }
}
break;
@@ -812,22 +946,27 @@ static void *nxplayer_playthread(pthread_addr_t pvarg)
case AUDIO_MSG_STOP:
/* Send a stop message to the device */
- audvdbg("Stopping!\n");
+ audvdbg("Stopping! outstanding=%d\n", outstanding);
#ifdef CONFIG_AUDIO_MULTI_SESSION
ioctl(pPlayer->devFd, AUDIOIOC_STOP,
- (unsigned long) pPlayer->session);
+ (unsigned long) pPlayer->session);
#else
ioctl(pPlayer->devFd, AUDIOIOC_STOP, 0);
#endif
+ /* Stay in the running loop (without sending more data).
+ * we will need to recover our audio buffers. We will
+ * loop until AUDIO_MSG_COMPLETE is received.
+ */
+
streaming = false;
- running = false;
break;
/* Message indicating the playback is complete */
case AUDIO_MSG_COMPLETE:
- audvdbg("Play complete\n");
+ audvdbg("Play complete. outstanding=%d\n", outstanding);
+ DEBUGASSERT(outstanding == 0);
running = false;
break;
@@ -897,6 +1036,7 @@ err_out:
fclose(pPlayer->fileFd); /* Close the file */
pPlayer->fileFd = NULL; /* Clear out the FD */
}
+
close(pPlayer->devFd); /* Close the device */
pPlayer->devFd = -1; /* Mark device as closed */
mq_close(pPlayer->mq); /* Close the message queue */