blob: 4b63ed9e7677789e1f85ca6f10cda472fb857523 [file] [log] [blame]
From 5e87cad41e7762ee5a097b4be451a3415b763229 Mon Sep 17 00:00:00 2001
From: Hidalgo Huang <hidalgo.huang@mediatek.com>
Date: Wed, 19 Dec 2018 16:34:31 +0800
Subject: [PATCH 2/9] cplay: add aac playback support
Support ADTS/ADIF/LOAS/LATM/RAW files.
Signed-off-by: Hidalgo Huang <hidalgo.huang@mediatek.com>
---
include/tinycompress/tinyaac.h | 77 ++++++++++++++
src/utils/cplay.c | 187 ++++++++++++++++++++++++++++++++-
2 files changed, 260 insertions(+), 4 deletions(-)
create mode 100644 include/tinycompress/tinyaac.h
diff --git a/include/tinycompress/tinyaac.h b/include/tinycompress/tinyaac.h
new file mode 100644
index 0000000..c5ee94e
--- /dev/null
+++ b/include/tinycompress/tinyaac.h
@@ -0,0 +1,77 @@
+/*
+ * This file is provided under a dual BSD/LGPLv2.1 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * BSD LICENSE
+ *
+ * aac header and prasing
+ * Copyright (c) 2018, MediaTek Inc.
+ * All rights reserved.
+ *
+ * Author: Hidalgo Huang <hidalgo.huang@mediatek.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * LGPL LICENSE
+ *
+ * aac header and parsing
+ * Copyright (c) 2018, MediaTek inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to
+ * the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef __TINYAAC_H
+#define __TINYAAC_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define ADTS_HEADER_PARSE_SIZE (4)
+#define LOAS_HEADER_PARSE_SIZE (2)
+#define ADIF_HEADER_PARSE_SIZE (4)
+
+static const int aac_sample_rates[16] = {
+ 96000, 88200, 64000, 48000,
+ 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000,
+ 73500, 0, 0, 0,
+};
+
+#endif
diff --git a/src/utils/cplay.c b/src/utils/cplay.c
index 3e9fcac..8cb86c6 100644
--- a/src/utils/cplay.c
+++ b/src/utils/cplay.c
@@ -72,6 +72,7 @@
#include "sound/compress_params.h"
#include "tinycompress/tinycompress.h"
#include "tinycompress/tinymp3.h"
+#include "tinycompress/tinyaac.h"
static int verbose;
static const unsigned int DEFAULT_CODEC_ID = SND_AUDIOCODEC_PCM;
@@ -112,6 +113,8 @@ static void usage(void)
"-f\tfragments\n\n"
"-v\tverbose mode\n"
"-h\tPrints this help list\n\n"
+ "-R\tspecify sample rate\n\n"
+ "-C\tspecify channel count\n\n"
"Example:\n"
"\tcplay -c 1 -d 2 test.mp3\n"
"\tcplay -f 5 test.mp3\n\n"
@@ -128,7 +131,8 @@ static void usage(void)
void play_samples(char *name, unsigned int card, unsigned int device,
unsigned long buffer_size, unsigned int frag,
- unsigned long codec_id);
+ unsigned long codec_id, unsigned int rate,
+ unsigned int channels);
struct mp3_header {
uint16_t sync;
@@ -165,6 +169,143 @@ static int parse_mp3_header(struct mp3_header *header, unsigned int *num_channel
return 0;
}
+static int check_aac_adts_frame(FILE *file, unsigned int *num_channels,
+ unsigned int *sample_rate, unsigned int *format, char *file_name)
+{
+ size_t read;
+ unsigned char data[ADTS_HEADER_PARSE_SIZE];
+ unsigned int freq_index;
+
+ read = fread(&data, 1, sizeof(data), file);
+ if (read != sizeof(data))
+ return -1;
+
+ // check syncword
+ if ((data[0] == 0xff) && ((data[1] & 0xf6) == 0xf0)) {
+ *format = (data[1] & 0x8) ? SND_AUDIOSTREAMFORMAT_MP2ADTS :
+ SND_AUDIOSTREAMFORMAT_MP4ADTS;
+ freq_index = (data[2] >> 2) & 0xf;
+ *sample_rate = aac_sample_rates[freq_index];
+ *num_channels = (data[2] & 0x1) << 2 | (data[3] >> 6);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int check_aac_loas_frame(FILE *file, unsigned int *num_channels,
+ unsigned int *sample_rate, unsigned int *format, char *file_name)
+{
+ size_t read;
+ unsigned char data[LOAS_HEADER_PARSE_SIZE];
+
+ read = fread(&data, 1, sizeof(data), file);
+ if (read != sizeof(data))
+ return -1;
+
+ // check AudioSyncStream syncword 0x2b7 (11bits)
+ if ((data[0] == 0x56) && ((data[1] & 0xe0) == 0xe0)) {
+ *format = SND_AUDIOSTREAMFORMAT_MP4LOAS;
+ *num_channels = 2;
+ if (*sample_rate == 0)
+ *sample_rate = 44100;
+ if (*num_channels == 0)
+ *num_channels = 2;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int check_aac_adif_frame(FILE *file, unsigned int *num_channels,
+ unsigned int *sample_rate, unsigned int *format, char *file_name)
+{
+ size_t read;
+ unsigned char data[ADIF_HEADER_PARSE_SIZE];
+
+ read = fread(&data, 1, sizeof(data), file);
+ if (read != sizeof(data))
+ return -1;
+
+ // check syncword
+ if ((data[0] == 'A') && (data[1] == 'D') &&
+ (data[2] == 'I') && (data[3] == 'F') ) {
+ *format = SND_AUDIOSTREAMFORMAT_ADIF;
+ if (*sample_rate == 0)
+ *sample_rate = 44100;
+ if (*num_channels == 0)
+ *num_channels = 2;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int check_aac_latm_frame(FILE *file, unsigned int *num_channels,
+ unsigned int *sample_rate, unsigned int *format, char *file_name)
+{
+ if (strstr(file_name, ".latm")) {
+ *format = SND_AUDIOSTREAMFORMAT_MP4LATM;
+ if (*sample_rate == 0)
+ *sample_rate = 44100;
+ if (*num_channels == 0)
+ *num_channels = 2;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int check_aac_raw_frame(FILE *file, unsigned int *num_channels,
+ unsigned int *sample_rate, unsigned int *format, char *file_name)
+{
+ if (strstr(file_name, ".aac")) {
+ *format = SND_AUDIOSTREAMFORMAT_RAW;
+ if (*sample_rate == 0)
+ *sample_rate = 44100;
+ if (*num_channels == 0)
+ *num_channels = 2;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int parse_aac_header(FILE *file, unsigned int *num_channels,
+ unsigned int *sample_rate, unsigned int *format, char *file_name)
+{
+ int i;
+ fpos_t init_pos;
+ static int (*aac_parsers[])(FILE *,
+ unsigned int *,
+ unsigned int *,
+ unsigned int *,
+ char *) = {
+ check_aac_adts_frame,
+ check_aac_loas_frame,
+ check_aac_adif_frame,
+ check_aac_latm_frame,
+ check_aac_raw_frame,
+ };
+ int parser_num = sizeof(aac_parsers) / sizeof(aac_parsers[0]);
+
+ fgetpos(file, &init_pos);
+
+ for (i = 0; i < parser_num; i++) {
+ fsetpos(file, &init_pos);
+
+ if (aac_parsers[i](file, num_channels, sample_rate, format, file_name) == 0)
+ goto aac_parsed;
+ }
+
+aac_err:
+ return -1;
+
+aac_parsed:
+ fsetpos(file, &init_pos);
+ return 0;
+}
+
static int print_time(struct compress *compress)
{
unsigned int avail;
@@ -186,12 +327,13 @@ int main(int argc, char **argv)
int c, i;
unsigned int card = 0, device = 0, frag = 0;
unsigned int codec_id = SND_AUDIOCODEC_MP3;
+ unsigned int rate = 0, channels = 0;
if (argc < 2)
usage();
verbose = 0;
- while ((c = getopt(argc, argv, "hvb:f:c:d:I:")) != -1) {
+ while ((c = getopt(argc, argv, "hvb:f:c:d:I:R:C:")) != -1) {
switch (c) {
case 'h':
usage();
@@ -230,6 +372,12 @@ int main(int argc, char **argv)
case 'v':
verbose = 1;
break;
+ case 'R':
+ rate = strtol(optarg, NULL, 10);
+ break;
+ case 'C':
+ channels = strtol(optarg, NULL, 10);
+ break;
default:
exit(EXIT_FAILURE);
}
@@ -239,7 +387,7 @@ int main(int argc, char **argv)
file = argv[optind];
- play_samples(file, card, device, buffer_size, frag, codec_id);
+ play_samples(file, card, device, buffer_size, frag, codec_id, rate, channels);
fprintf(stderr, "Finish Playing.... Close Normally\n");
exit(EXIT_SUCCESS);
@@ -297,9 +445,37 @@ void get_codec_iec(FILE *file, struct compr_config *config,
codec->format = 0;
}
+void get_codec_aac(FILE *file, struct compr_config *config,
+ struct snd_codec *codec, char *file_name,
+ unsigned int specified_rate,
+ unsigned int specified_channels)
+{
+ unsigned int channels, rate, format;
+
+ rate = specified_rate;
+ channels = specified_channels;
+
+ if (parse_aac_header(file, &channels, &rate, &format, file_name) == -1) {
+ fclose(file);
+ exit(EXIT_FAILURE);
+ }
+
+ codec->id = SND_AUDIOCODEC_AAC;
+ codec->ch_in = channels;
+ codec->ch_out = channels;
+ codec->sample_rate = rate;
+ codec->bit_rate = 0;
+ codec->rate_control = 0;
+ codec->profile = SND_AUDIOPROFILE_AAC;
+ codec->level = 0;
+ codec->ch_mode = 0;
+ codec->format = format;
+}
+
void play_samples(char *name, unsigned int card, unsigned int device,
unsigned long buffer_size, unsigned int frag,
- unsigned long codec_id)
+ unsigned long codec_id, unsigned int rate,
+ unsigned int channels)
{
struct compr_config config;
struct snd_codec codec;
@@ -323,6 +499,9 @@ void play_samples(char *name, unsigned int card, unsigned int device,
case SND_AUDIOCODEC_IEC61937:
get_codec_iec(file, &config, &codec);
break;
+ case SND_AUDIOCODEC_AAC:
+ get_codec_aac(file, &config, &codec, name, rate, channels);
+ break;
default:
fprintf(stderr, "codec ID %ld is not supported\n", codec_id);
exit(EXIT_FAILURE);
--
2.18.0