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