diff --git a/include/system/nxplayer.h b/include/system/nxplayer.h index 19e154594..cc8e01879 100644 --- a/include/system/nxplayer.h +++ b/include/system/nxplayer.h @@ -39,6 +39,14 @@ * Public Type Declarations ****************************************************************************/ +struct nxplayer_dec_ops_s +{ + int format; + CODE int (*pre_parse)(int fd, FAR uint32_t *samplerate, + FAR uint8_t *chans, FAR uint8_t *bps); + CODE int (*fill_data)(int fd, FAR struct ap_buffer_s *apb); +}; + /* This structure describes the internal state of the NxPlayer */ struct nxplayer_s @@ -72,6 +80,8 @@ struct nxplayer_s uint16_t treble; /* Treble as a whole % */ uint16_t bass; /* Bass as a whole % */ #endif + + FAR const struct nxplayer_dec_ops_s *ops; }; typedef int (*nxplayer_func)(FAR struct nxplayer_s *pplayer, char *pargs); @@ -480,6 +490,53 @@ int nxplayer_settreble(FAR struct nxplayer_s *pplayer, uint8_t treble); int nxplayer_systemreset(FAR struct nxplayer_s *pplayer); #endif +/**************************************************************************** + * Name: nxplayer_parse_mp3 + * + * Performs pre-process when play mp3 file. + * Parse samplerate, channels, bps. + * + * Input Parameters: + * pplayer - Pointer to the context to initialize + * + * Returned Value: + * OK if file parsed successfully. + * + ****************************************************************************/ + +int nxplayer_parse_mp3(int fd, FAR uint32_t *samplerate, + FAR uint8_t *chans, FAR uint8_t *bps); + +/**************************************************************************** + * Name: nxplayer_fill_mp3 + * + * Performs read mp3 frame to apb buffer + * + * Input Parameters: + * pplayer - Pointer to the context to initialize + * + * Returned Value: + * OK if file read successfully. + * + ****************************************************************************/ + +int nxplayer_fill_mp3(int fd, FAR struct ap_buffer_s *apb); + +/**************************************************************************** + * Name: nxplayer_fill_pcm + * + * Performs read pcm file to apb buffer + * + * Input Parameters: + * pplayer - Pointer to the context to initialize + * + * Returned Value: + * OK if file read successfully. + * + ****************************************************************************/ + +int nxplayer_fill_pcm(int fd, FAR struct ap_buffer_s *apb); + #undef EXTERN #ifdef __cplusplus } diff --git a/system/nxplayer/Makefile b/system/nxplayer/Makefile index 5f9f49485..73241e66f 100644 --- a/system/nxplayer/Makefile +++ b/system/nxplayer/Makefile @@ -23,6 +23,8 @@ include $(APPDIR)/Make.defs # NxPlayer Library CSRCS = nxplayer.c +CSRCS += nxplayer_mp3.c +CSRCS += nxplayer_pcm.c ifneq ($(CONFIG_NXPLAYER_COMMAND_LINE),) PROGNAME = nxplayer diff --git a/system/nxplayer/nxplayer.c b/system/nxplayer/nxplayer.c index 64665db31..ea80e1e44 100644 --- a/system/nxplayer/nxplayer.c +++ b/system/nxplayer/nxplayer.c @@ -88,6 +88,10 @@ struct nxplayer_ext_fmt_s int nxplayer_getmidisubformat(int fd); #endif +#ifdef CONFIG_AUDIO_FORMAT_MP3 +int nxplayer_getmp3subformat(int fd); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -99,7 +103,7 @@ static const struct nxplayer_ext_fmt_s g_known_ext[] = { "ac3", AUDIO_FMT_AC3, NULL }, #endif #ifdef CONFIG_AUDIO_FORMAT_MP3 - { "mp3", AUDIO_FMT_MP3, NULL }, + { "mp3", AUDIO_FMT_MP3, nxplayer_getmp3subformat }, #endif #ifdef CONFIG_AUDIO_FORMAT_DTS { "dts", AUDIO_FMT_DTS, NULL }, @@ -121,6 +125,21 @@ static const struct nxplayer_ext_fmt_s g_known_ext[] = static const int g_known_ext_count = sizeof(g_known_ext) / sizeof(struct nxplayer_ext_fmt_s); + +static const struct nxplayer_dec_ops_s g_dec_ops[] = +{ + { + AUDIO_FMT_MP3, + nxplayer_parse_mp3, + nxplayer_fill_mp3 + }, + { + AUDIO_FMT_PCM, + NULL, + nxplayer_fill_pcm + } +}; + #endif /* CONFIG_NXPLAYER_FMT_FROM_EXT */ /**************************************************************************** @@ -501,6 +520,11 @@ int nxplayer_getmidisubformat(int fd) } #endif +int nxplayer_getmp3subformat(int fd) +{ + return AUDIO_SUBFMT_PCM_MP3; +} + /**************************************************************************** * Name: nxplayer_fmtfromextension * @@ -608,6 +632,8 @@ static int nxplayer_mediasearch(FAR struct nxplayer_s *pplayer, static int nxplayer_readbuffer(FAR struct nxplayer_s *pplayer, FAR struct ap_buffer_s *apb) { + 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. @@ -622,65 +648,17 @@ static int nxplayer_readbuffer(FAR struct nxplayer_s *pplayer, return -ENODATA; } - /* Read data into the buffer. */ - - apb->nbytes = read(pplayer->fd, apb->samp, apb->nmaxbytes); - apb->curbyte = 0; - apb->flags = 0; - -#ifdef CONFIG_NXPLAYER_HTTP_STREAMING_SUPPORT - /* read data up to nmaxbytes from network */ - - while (0 < apb->nbytes && apb->nbytes < apb->nmaxbytes) + ret = pplayer->ops->fill_data(pplayer->fd, apb); + if (ret < 0) { - int n = apb->nmaxbytes - apb->nbytes; - int ret = read(pplayer->fd, &apb->samp[apb->nbytes], n); - - if (0 >= ret) - { - break; - } - - apb->nbytes += ret; - } -#endif - - if (apb->nbytes < apb->nmaxbytes) - { -#if defined (CONFIG_DEBUG_AUDIO_INFO) || defined (CONFIG_DEBUG_AUDIO_ERROR) - int errcode = errno; - - audinfo("Closing audio file, nbytes=%d errcode=%d\n", - apb->nbytes, errcode); -#endif - /* End of file or read error.. We are finished with this file in any * event. */ close(pplayer->fd); pplayer->fd = -1; - - /* Set a flag to indicate that this is the final buffer in the stream */ - - apb->flags |= AUDIO_APB_FINAL; - -#ifdef CONFIG_DEBUG_AUDIO_ERROR - /* Was this a file read error */ - - if (apb->nbytes == 0 && errcode != 0) - { - DEBUGASSERT(errcode > 0); - auderr("ERROR: fread failed: %d\n", errcode); - } -#endif } - /* 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; } @@ -1150,6 +1128,7 @@ err_out: pplayer->dev_fd = -1; /* Mark device as closed */ mq_close(pplayer->mq); /* Close the message queue */ mq_unlink(pplayer->mqname); /* Unlink the message queue */ + pplayer->ops = NULL; /* Clear offload parser */ pplayer->state = NXPLAYER_STATE_IDLE; /* Go to IDLE */ sem_post(&pplayer->sem); /* Release the semaphore */ @@ -1780,6 +1759,7 @@ static int nxplayer_playinternal(FAR struct nxplayer_s *pplayer, #endif int tmpsubfmt = AUDIO_FMT_UNDEF; int ret; + int c; DEBUGASSERT(pplayer != NULL); DEBUGASSERT(pfilename != NULL); @@ -1877,6 +1857,26 @@ static int nxplayer_playinternal(FAR struct nxplayer_s *pplayer, goto err_out_nodev; } + for (c = 0; c < sizeof(g_dec_ops) / sizeof(g_dec_ops[0]); c++) + { + if (g_dec_ops[c].format == filefmt) + { + pplayer->ops = &g_dec_ops[c]; + break; + } + } + + if (!pplayer->ops) + { + goto err_out; + } + + if (pplayer->ops->pre_parse) + { + ret = pplayer->ops->pre_parse(pplayer->fd, &samprate, + &nchannels, &bpsamp); + } + /* Try to reserve the device */ #ifdef CONFIG_AUDIO_MULTI_SESSION @@ -1906,6 +1906,7 @@ static int nxplayer_playinternal(FAR struct nxplayer_s *pplayer, cap_desc.caps.ac_controls.hw[0] = samprate; cap_desc.caps.ac_controls.b[3] = samprate >> 16; cap_desc.caps.ac_controls.b[2] = bpsamp; + cap_desc.caps.ac_subtype = filefmt; ioctl(pplayer->dev_fd, AUDIOIOC_CONFIGURE, (unsigned long)&cap_desc); } diff --git a/system/nxplayer/nxplayer_mp3.c b/system/nxplayer/nxplayer_mp3.c new file mode 100644 index 000000000..f5445ec02 --- /dev/null +++ b/system/nxplayer/nxplayer_mp3.c @@ -0,0 +1,311 @@ +/**************************************************************************** + * apps/system/nxplayer/nxplayer_mp3.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include + +#include "system/nxplayer.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define ID3V2_BIT_MASK 0x7F + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +const static uint16_t g_mpa_freq_tab[3] = +{ + 44100, 48000, 32000 +}; + +const static uint16_t g_mpa_bitrate_tab[2][3][15] = +{ + { + { + 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 + }, + { + 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 + }, + { + 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 + } + }, + { + { + 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 + }, + { + 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 + }, + { + 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 + } + } +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int nxplayer_check_mpeg(uint32_t header) +{ + /* header */ + + if ((header & 0xffe00000) != 0xffe00000) + { + return -EINVAL; + } + + /* version check */ + + if ((header & (3 << 19)) == 1 << 19) + { + return -EINVAL; + } + + /* layer check */ + + if ((header & (3 << 17)) == 0) + { + return -EINVAL; + } + + /* bit rate */ + + if ((header & (0xf << 12)) == 0xf << 12) + { + return -EINVAL; + } + + /* frequency */ + + if ((header & (3 << 10)) == 3 << 10) + { + return -EINVAL; + } + + return 0; +} + +static int nxplayer_parse_mpeg(uint32_t header, FAR uint32_t *samplerate, + FAR uint8_t *chans, FAR uint8_t *bps) +{ + int sample_rate; + int frame_size; + int padding; + int mpeg25; + int sr_idx; + int br_idx; + int layer; + int mode; + int lsf; + int ret; + + ret = nxplayer_check_mpeg(header); + if (ret < 0) + { + return ret; + } + + if (header & (1 << 20)) + { + lsf = (header & (1 << 19)) ? 0 : 1; + mpeg25 = 0; + } + else + { + lsf = 1; + mpeg25 = 1; + } + + layer = 4 - ((header >> 17) & 3); + br_idx = (header >> 12) & 0xf; + sr_idx = (header >> 10) & 3; + padding = (header >> 9) & 1; + mode = (header >> 6) & 3; + + if (sr_idx >= sizeof(g_mpa_freq_tab) / sizeof(g_mpa_freq_tab[0]) || + br_idx >= 0xf) + { + return -EINVAL; + } + + sample_rate = g_mpa_freq_tab[sr_idx] >> (lsf + mpeg25); + + if (br_idx != 0) + { + frame_size = g_mpa_bitrate_tab[lsf][layer - 1][br_idx]; + + switch (layer) + { + case 1: + frame_size = (frame_size * 12000) / sample_rate; + frame_size = (frame_size + padding) * 4; + break; + + case 2: + frame_size = (frame_size * 144000) / sample_rate; + frame_size += padding; + break; + + default: + case 3: + frame_size = (frame_size * 144000) / (sample_rate << lsf); + frame_size += padding; + break; + } + } + else + { + /* if no frame size computed, signal it */ + + return -EINVAL; + } + + if (samplerate) + { + *samplerate = sample_rate; + } + + if (chans) + { + *chans = mode == 3 ? 1 : 2; + } + + if (bps) + { + *bps = 16; + } + + return frame_size; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxplayer_parse_mp3 + * + * nxplayer_parse_mp3() parse mp3 header, get samplerate, channels, bps. + * + ****************************************************************************/ + +int nxplayer_parse_mp3(int fd, FAR uint32_t *samplerate, + FAR uint8_t *chans, FAR uint8_t *bps) +{ + uint32_t mpa_header; + uint8_t buffer[10]; + off_t position; + int ret; + + ret = read(fd, buffer, sizeof(buffer)); + if (ret < sizeof(buffer)) + { + return -ENODATA; + } + + position = (buffer[6] & ID3V2_BIT_MASK) * 0x200000 + + (buffer[7] & ID3V2_BIT_MASK) * 0x4000 + + (buffer[8] & ID3V2_BIT_MASK) * 0x80 + + (buffer[9] & ID3V2_BIT_MASK) + + sizeof(buffer); + + lseek(fd, position, SEEK_SET); + + read(fd, buffer, 4); + mpa_header = buffer[0] << 24 | + buffer[1] << 16 | + buffer[2] << 8 | + buffer[3]; + + ret = nxplayer_parse_mpeg(mpa_header, samplerate, chans, bps); + if (ret < 0) + { + return ret; + } + + lseek(fd, position, SEEK_SET); + return OK; +} + +/**************************************************************************** + * Name: nxplayer_fill_mp3 + * + * nxplayer_fill_mp3 fill mp3 data into apb buffer. + * + ****************************************************************************/ + +int nxplayer_fill_mp3(int fd, FAR struct ap_buffer_s *apb) +{ + uint32_t mpa_header; + uint8_t header[16]; + int h_size = 4; + int b_size; + int size; + int ret; + + ret = read(fd, header, h_size); + if (ret < h_size) + { + return -ENODATA; + } + + mpa_header = header[0] << 24 | + header[1] << 16 | + header[2] << 8 | + header[3]; + + size = nxplayer_parse_mpeg(mpa_header, NULL, NULL, NULL); + if (size < 0) + { + return size; + } + + memcpy(apb->samp, header, h_size); + + b_size = size - h_size; + ret = read(fd, apb->samp + h_size, b_size + 8); + if (ret < b_size) + { + return -ENODATA; + } + + lseek(fd, -8, SEEK_CUR); + + apb->nbytes = size + 8; + apb->curbyte = 0; + apb->flags = 0; + + return OK; +} diff --git a/system/nxplayer/nxplayer_pcm.c b/system/nxplayer/nxplayer_pcm.c new file mode 100644 index 000000000..2b40b6959 --- /dev/null +++ b/system/nxplayer/nxplayer_pcm.c @@ -0,0 +1,104 @@ +/**************************************************************************** + * apps/system/nxplayer/nxplayer_pcm.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include "system/nxplayer.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxplayer_fill_pcm + * + * nxplayer_fill_pcm fill pcm data into apb buffer. + * + ****************************************************************************/ + +int nxplayer_fill_pcm(int fd, FAR struct ap_buffer_s *apb) +{ + /* Read data into the buffer. */ + + apb->nbytes = read(fd, apb->samp, apb->nmaxbytes); + apb->curbyte = 0; + apb->flags = 0; + +#ifdef CONFIG_NXPLAYER_HTTP_STREAMING_SUPPORT + /* read data up to nmaxbytes from network */ + + while (0 < apb->nbytes && apb->nbytes < apb->nmaxbytes) + { + int n = apb->nmaxbytes - apb->nbytes; + int ret = read(fd, &apb->samp[apb->nbytes], n); + + if (0 >= ret) + { + break; + } + + apb->nbytes += ret; + } +#endif + + if (apb->nbytes < apb->nmaxbytes) + { +#if defined (CONFIG_DEBUG_AUDIO_INFO) || defined (CONFIG_DEBUG_AUDIO_ERROR) + int errcode = errno; + + audinfo("Closing audio file, nbytes=%d errcode=%d\n", + apb->nbytes, errcode); +#endif + + /* Set a flag to indicate that this is the final buffer in the stream */ + + apb->flags |= AUDIO_APB_FINAL; + +#ifdef CONFIG_DEBUG_AUDIO_ERROR + /* Was this a file read error */ + + if (apb->nbytes == 0 && errcode != 0) + { + DEBUGASSERT(errcode > 0); + auderr("ERROR: fread failed: %d\n", errcode); + } +#endif + + return -ENODATA; + } + + /* 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; +}