qahw: test: Enhance and add new functionality to playback test app
- add mp3 & aac compress playback support
- move arguments to linux command like and take all
args while invoking the test app
- add volume support
- align all the code to c style
- add default parameters
Change-Id: Ie5aa0b741887cddc3d6eb58d2ef5c9c93bc4e22d
diff --git a/qahw_api/test/qahw_playback_test.c b/qahw_api/test/qahw_playback_test.c
index 8426945..f334719 100644
--- a/qahw_api/test/qahw_playback_test.c
+++ b/qahw_api/test/qahw_playback_test.c
@@ -18,188 +18,471 @@
/* Test app to play audio at the HAL layer */
+#include <getopt.h>
+#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "qahw_api.h"
#include "qahw_defs.h"
+
#define nullptr NULL
-#define WAV 1
-#define MP3 2
+FILE * log_file = NULL;
+const char *log_filename = NULL;
+float vol_level = 0.01;
+
+enum {
+ FILE_WAV = 1,
+ FILE_MP3,
+ FILE_AAC,
+ FILE_AAC_ADTS
+};
+
+typedef enum {
+ AAC_LC = 1,
+ AAC_HE_V1,
+ AAC_HE_V2
+} aac_format_type_t;
+
+static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t write_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t drain_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t drain_cond = PTHREAD_COND_INITIALIZER;
+
+
+int async_callback(qahw_stream_callback_event_t event, void *param,
+ void *cookie)
+{
+ switch (event) {
+ case QAHW_STREAM_CBK_EVENT_WRITE_READY:
+ fprintf(log_file, "QAHW_STREAM_CBK_EVENT_DRAIN_READY\n");
+ pthread_mutex_lock(&write_lock);
+ pthread_cond_signal(&write_cond);
+ pthread_mutex_unlock(&write_lock);
+ break;
+ case QAHW_STREAM_CBK_EVENT_DRAIN_READY:
+ fprintf(log_file, "QAHW_STREAM_CBK_EVENT_DRAIN_READY\n");
+ pthread_mutex_lock(&drain_lock);
+ pthread_cond_signal(&drain_cond);
+ pthread_mutex_unlock(&drain_lock);
+ default:
+ break;
+ }
+ return 0;
+}
+
+
+int write_to_hal(qahw_stream_handle_t* out_handle, char *data,
+ size_t bytes)
+{
+ ssize_t ret;
+ pthread_mutex_lock(&write_lock);
+ qahw_out_buffer_t out_buf;
+
+ memset(&out_buf,0, sizeof(qahw_out_buffer_t));
+ out_buf.buffer = data;
+ out_buf.bytes = bytes;
+
+ ret = qahw_out_write(out_handle, &out_buf);
+ if (ret < 0 || ret == bytes) {
+ fprintf(log_file, "Writing data to hal failed or full write %ld, %ld\n",
+ ret, bytes);
+ } else if (ret != bytes) {
+ fprintf(log_file, "ret %ld, bytes %ld\n", ret, bytes);
+ fprintf(log_file, "Waiting for event write ready\n");
+ pthread_cond_wait(&write_cond, &write_lock);
+ fprintf(log_file, "out of wait for event write ready\n");
+ }
+
+ pthread_mutex_unlock(&write_lock);
+ return ret;
+}
/* Play audio from a WAV file.
+ *
+ * Parameters:
+ * out_stream: A pointer to the output audio stream.
+ * in_file: A pointer to a SNDFILE object.
+ * config: A pointer to struct that contains audio configuration data.
+ *
+ * Returns: An int which has a non-negative number on success.
+ */
- Parameters:
- out_stream: A pointer to the output audio stream.
- in_file: A pointer to a SNDFILE object.
- config: A pointer to struct that contains audio configuration data.
+int play_file(qahw_stream_handle_t* out_handle, FILE* in_file,
+ bool is_offload) {
+ int rc = 0;
+ int offset = 0;
+ size_t bytes_wanted = 0;
+ size_t write_length = 0;
+ size_t bytes_remaining = 0;
+ size_t bytes_written = 0;
+ size_t bytes_read = 0;
+ char *data = NULL;
+ qahw_out_buffer_t out_buf;
+ bool exit = false;
- Returns: An int which has a non-negative number on success.
-*/
+ if (is_offload) {
+ fprintf(log_file, "Set callback for offload stream\n");
+ qahw_out_set_callback(out_handle, async_callback, NULL);
+ }
-int play_file(qahw_stream_handle_t* out_handle, FILE* in_file) {
+ rc = qahw_out_set_volume(out_handle, vol_level, vol_level);
+ if (rc < 0)
+ fprintf(log_file, "unable to set volume");
- int rc = 0;
- size_t frames_read = 1;
- size_t bytes_wanted ;
- char *data = NULL;
- qahw_out_buffer_t out_buf;
+ bytes_wanted = qahw_out_get_buffer_size(out_handle);
+ data = (char *) malloc (bytes_wanted);
+ if (data == NULL) {
+ fprintf(log_file, "calloc failed!!\n");
+ return -ENOMEM;
+ }
- bytes_wanted = qahw_out_get_buffer_size(out_handle);
- data = (char *) malloc (bytes_wanted);
- if (data == NULL) {
- printf("calloc failed!!\n");
- return -ENOMEM;
- }
+ while (!exit) {
+ if (!bytes_remaining) {
+ bytes_read = fread(data, 1, bytes_wanted, in_file);
+ fprintf(log_file, "fread from file %ld\n", bytes_read);
+ if (bytes_read <= 0) {
+ if (feof(in_file)) {
+ fprintf(log_file, "End of file");
+ if (is_offload) {
+ pthread_mutex_lock(&drain_lock);
+ if (is_offload) {
+ qahw_out_drain(out_handle, QAHW_DRAIN_ALL);
+ pthread_cond_wait(&drain_cond, &drain_lock);
+ fprintf(log_file, "Out of compress drain\n");
+ }
+ pthread_mutex_unlock(&drain_lock);
+ }
+ } else {
+ fprintf(log_file, "Error in fread --%d\n", ferror(in_file));
+ fprintf(stderr, "Error in fread --%d\n", ferror(in_file));
+ }
+ exit = true;
+ continue;
+ }
+ bytes_remaining = write_length = bytes_read;
+ }
- while(frames_read != 0) {
- frames_read = fread(data, bytes_wanted , 1, in_file);
- if (frames_read < 1) {
- if (feof(in_file))
- break;
- else
- printf("Error in fread --%d\n",ferror(in_file));
- }
- memset(&out_buf,0, sizeof(qahw_out_buffer_t));
- out_buf.buffer = data;
- out_buf.bytes = frames_read * bytes_wanted;
- rc = qahw_out_write(out_handle, &out_buf);
- if (rc < 0) {
- printf("Writing data to hal failed %d \n",rc);
- break;
- }
- }
- return rc;
+ offset = write_length - bytes_remaining;
+ fprintf(log_file, "bytes_remaining %ld, offset %d, write length %ld\n",
+ bytes_remaining, offset, write_length);
+ bytes_written = write_to_hal(out_handle, data+offset, bytes_remaining);
+ bytes_remaining -= bytes_written;
+ fprintf(log_file, "bytes_written %ld, bytes_remaining %ld\n",
+ bytes_written, bytes_remaining);
+ }
+
+ return rc;
}
-// Prints usage information if input arguments are missing.
-void Usage() {
- fprintf(stderr, "Usage:hal_play [device] [filename] [filetype]\n"
- "device: hex value representing the audio device (see "
- "system/media/audio/include/system/audio.h)\n"
- "filename must be passed as an argument.\n"
- "filetype (1:WAV 2:MP3) \n");
+bool is_valid_aac_format_type(aac_format_type_t format_type)
+{
+ bool valid_format_type = false;
+
+ switch (format_type) {
+ case AAC_LC:
+ case AAC_HE_V1:
+ case AAC_HE_V2:
+ valid_format_type = true;
+ break;
+ default:
+ break;
+ }
+ return valid_format_type;
+}
+
+/*
+ * Obtain aac format (refer audio.h) for format type entered.
+ */
+
+audio_format_t get_aac_format(int filetype, aac_format_type_t format_type)
+{
+ audio_format_t aac_format = AUDIO_FORMAT_AAC_ADTS_LC; /* default aac frmt*/
+
+ if (filetype == FILE_AAC_ADTS) {
+ switch (format_type) {
+ case AAC_LC:
+ aac_format = AUDIO_FORMAT_AAC_ADTS_LC;
+ break;
+ case AAC_HE_V1:
+ aac_format = AUDIO_FORMAT_AAC_ADTS_HE_V1;
+ break;
+ case AAC_HE_V2:
+ aac_format = AUDIO_FORMAT_AAC_ADTS_HE_V2;
+ break;
+ default:
+ break;
+ }
+ } else if (filetype == FILE_AAC) {
+ switch (format_type) {
+ case AAC_LC:
+ aac_format = AUDIO_FORMAT_AAC_LC;
+ break;
+ case AAC_HE_V1:
+ aac_format = AUDIO_FORMAT_AAC_HE_V1;
+ break;
+ case AAC_HE_V2:
+ aac_format = AUDIO_FORMAT_AAC_HE_V2;
+ break;
+ default:
+ break;
+ }
+ } else {
+ fprintf(log_file, "Invalid filetype provided %d\n", filetype);
+ fprintf(stderr, "Invalid filetype provided %d\n", filetype);
+ }
+
+ fprintf(log_file, "aac format %d\n", aac_format);
+ return aac_format;
+}
+
+void usage() {
+ printf(" \n Command \n");
+ printf(" \n hal_play_test <file path> - path of file to be played\n");
+ printf(" \n Options\n");
+ printf(" -r --sample-rate <sampling rate> - Required for Non-WAV streams\n");
+ printf(" For AAC-HE pls specify half the sample rate\n\n");
+ printf(" -c --channel count <channels> - Required for Non-WAV streams\n\n");
+ printf(" -v --volume <float volume level> - Volume level float value between 0.0 - 1.0.\n");
+ printf(" -d --device <decimal value> - see system/media/audio/include/system/audio.h for device values\n");
+ printf(" Optional Argument and Default value is 2, i.e Speaker\n\n");
+ printf(" -t --file-type <file type> - 1:WAV 2:MP3 3:AAC 4:AAC_ADTS\n");
+ printf(" Required for non WAV formats\n\n");
+ printf(" -a --aac-type <aac type> - Required for AAC streams\n");
+ printf(" 1: LC 2: HE_V1 3: HE_V2\n\n");
+ printf(" -l --log-file <FILEPATH> - File path for debug msg, to print\n");
+ printf(" on console use stdout or 1 \n\n");
+ printf(" \n Examples \n");
+ printf(" hal_play_test /etc/Anukoledenadu.wav -> plays Wav stream with default params\n\n");
+ printf(" hal_play_test /etc/MateRani.mp3 -t 2 -d 2 -v 0.01 -r 44100 -c 2 \n");
+ printf(" -> plays MP3 stream(-t = 2) on speaker device(-d = 2)\n");
+ printf(" -> 2 channels and 44100 sample rate\n\n");
+ printf(" hal_play_test /etc/AACLC-71-48000Hz-384000bps.aac -t 4 -d 2 -v 0.05 -r 48000 -c 2 -a 1 \n");
+ printf(" -> plays AAC-ADTS stream(-t = 4) on speaker device(-d = 2)\n");
+ printf(" -> AAC format type is LC(-a = 1)\n");
+ printf(" -> 2 channels and 48000 sample rate\n\n");
+ printf(" hal_play_test /etc/AACHE-adts-stereo-32000KHz-128000Kbps.aac -t 4 -d 2 -v 0.05 -r 16000 -c 2 -a 3 \n");
+ printf(" -> plays AAC-ADTS stream(-t = 4) on speaker device(-d = 2)\n");
+ printf(" -> AAC format type is HE V2(-a = 3)\n");
+ printf(" -> 2 channels and 16000 sample rate\n");
+ printf(" -> note that the sample rate is half the actual sample rate\n\n");
}
int main(int argc, char* argv[]) {
- if (argc < 4) {
- Usage();
- return -1;
- }
- // Process command line arguments.
- FILE *filestream = NULL;
- char header[44] = {0};
- int sample_rate = 0;
- int channels = 0;
- const int audio_device_base = 16;
- char* filename = nullptr;
- int filetype;
- qahw_module_handle_t *qahw_mod_handle;
- const char *mod_name = "audio.primary";
- uint32_t desired_output_device = strtol(
- argv[1], nullptr /* look at full string*/, audio_device_base);
+ FILE *file_stream = NULL;
+ char header[44] = {0};
+ char* filename = nullptr;
+ qahw_module_handle_t *qahw_mod_handle;
+ const char *mod_name = "audio.primary";
+ qahw_stream_handle_t* out_handle = nullptr;
+ int rc = 0;
- filename = argv[2];
- filetype = atoi (argv[3]);
+ /*
+ * Default values
+ */
+ int filetype = FILE_WAV;
+ int sample_rate = 44100;
+ int channels = 2;
+ const int audio_device_base = 0x2;/* spkr device*/
+ aac_format_type_t format_type = AAC_LC;
+ log_file = stdout;
+ audio_devices_t output_device = AUDIO_DEVICE_OUT_SPEAKER;
- printf("Starting audio hal tests.\n");
- int rc = 0;
+ struct option long_options[] = {
+ /* These options set a flag. */
+ {"device", required_argument, 0, 'd'},
+ {"sample-rate", required_argument, 0, 'r'},
+ {"channels", required_argument, 0, 'c'},
+ {"volume", required_argument, 0, 'v'},
+ {"log-file", required_argument, 0, 'l'},
+ {"file-type", required_argument, 0, 't'},
+ {"aac-type", required_argument, 0, 'a'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+ };
- qahw_mod_handle = qahw_load_module(mod_name);
+ int opt = 0;
+ int option_index = 0;
+ while ((opt = getopt_long(argc,
+ argv,
+ "-r:c:d:v:l::t:a:h",
+ long_options,
+ &option_index)) != -1) {
+ switch (opt) {
+ case 'r':
+ sample_rate = atoi(optarg);
+ break;
+ case 'c':;
+ channels = atoi(optarg);
+ break;
+ case 'd':
+ output_device = atoi(optarg);
+ break;
+ case 'v':
+ vol_level = atof(optarg);
+ break;
+ case 'l':
+ /*
+ * Fix Me: unable to log to a given file.
+ */
+ log_filename = optarg;
+ if((log_file = fopen(log_filename,"wb"))== NULL) {
+ fprintf(stderr, "Cannot open log file %s\n", log_filename);
+ /*
+ * continue to log to std out.
+ */
+ log_file = stdout;
+ }
- // Set to a high number so it doesn't interfere with existing stream handles
- audio_io_handle_t handle = 0x999;
- audio_devices_t output_device =
- (audio_devices_t)desired_output_device;
- audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
- audio_config_t config;
+ break;
+ case 't':
+ filetype = atoi(optarg);
+ break;
+ case 'a':
+ format_type = atoi(optarg);
+ break;
+ case 'h':
+ usage();
+ return 0;
+ break;
+ }
+ }
- memset(&config, 0, sizeof(audio_config_t));
+ filename = argv[1];
+ if((file_stream = fopen(filename, "r"))== NULL) {
+ fprintf(stderr, "Cannot Open Audio File %s\n", filename);
+ goto EXIT;
+ }
- if (filename) {
- printf("filename-----%s\n",filename);
- filestream = fopen (filename,"r");
- if (filestream == NULL) {
- printf("failed to open\n");
- exit(0);
- }
- }
+ /*
+ * Set to a high number so it doesn't interfere with existing stream handles
+ */
- switch (filetype) {
- case WAV:
- //Read the wave header
- rc = fread (header, 44 , 1, filestream);
- if (rc != 1) {
- printf("Error .Fread failed\n");
- exit(0);
- }
- if (strncmp (header,"RIFF",4) && strncmp (header+8, "WAVE",4)) {
- printf("Not a wave format\n");
- exit (1);
- }
- memcpy (&channels, &header[22], 2);
- memcpy (&sample_rate, &header[24], 4);
- config.channel_mask = audio_channel_out_mask_from_count(channels);
- config.offload_info.channel_mask = config.channel_mask;
- config.offload_info.sample_rate = sample_rate;
- config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
- break;
- case MP3:
- printf("Enter Number of channels:");
- scanf ("%d",&channels);
- config.channel_mask = audio_channel_out_mask_from_count(channels);
- printf("\nEnter Sample Rate:");
- scanf ("%d",&sample_rate);
- config.offload_info.channel_mask = config.channel_mask;
- config.offload_info.sample_rate = sample_rate;
- config.offload_info.format = AUDIO_FORMAT_MP3;
- break;
- default:
- printf("Does not support given filetype\n");
- Usage();
- exit (0);
- }
- config.offload_info.version = AUDIO_OFFLOAD_INFO_VERSION_CURRENT;
- config.offload_info.size = sizeof(audio_offload_info_t);
+ audio_io_handle_t handle = 0x999;
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
- printf("Now playing to output_device=%d sample_rate=%d \n",output_device,
- config.offload_info.sample_rate);
- const char* stream_name = "output_stream";
+ fprintf(stdout, "Playing:%s\n", filename);
+ fprintf(stdout, "File Type:%d\n", filetype);
+ fprintf(stdout, "Sample Rate:%d\n", sample_rate);
+ fprintf(stdout, "Channels:%d\n", channels);
+ fprintf(stdout, "Log file:%s\n", log_filename);
+ fprintf(stdout, "Volume level:%f\n", vol_level);
+ fprintf(stdout, "Output Device:%d\n", output_device);
+ fprintf(stdout, "Format Type:%d\n", format_type);
- // Open audio output stream.
- qahw_stream_handle_t* out_handle = nullptr;
- printf("calling open_out_put_stream:\n");
- rc = qahw_open_output_stream(qahw_mod_handle, handle, output_device,
- flags, &config, &out_handle,
- stream_name);
- printf("open output stream is sucess:%d out_handhle %p\n",rc,out_handle);
- if (rc) {
- printf("could not open output stream %d \n",rc);
- return -1;
- }
+ fprintf(stdout, "Starting audio hal tests.\n");
- play_file(out_handle, filestream);
+ qahw_mod_handle = qahw_load_module(mod_name);
- // Close output stream and device.
- rc = qahw_out_standby(out_handle);
- if (rc) {
- printf("out standby failed %d \n",rc);
- }
+ audio_config_t config;
+ memset(&config, 0, sizeof(audio_config_t));
- rc = qahw_close_output_stream(out_handle);
- if (rc) {
- printf("could not close output stream %d \n",rc);
- }
+ switch (filetype) {
+ case FILE_WAV:
+ /*
+ * Read the wave header
+ */
+ rc = fread (header, 44 , 1, file_stream);
+ if (rc != 1) {
+ fprintf(stdout, "Error .Fread failed\n");
+ exit(0);
+ }
+ if (strncmp (header, "RIFF", 4) && strncmp (header+8, "WAVE", 4)) {
+ fprintf(stdout, "Not a wave format\n");
+ exit (1);
+ }
+ memcpy (&channels, &header[22], 2);
+ memcpy (&sample_rate, &header[24], 4);
+ config.channel_mask = audio_channel_out_mask_from_count(channels);
+ config.offload_info.channel_mask = config.channel_mask;
+ config.offload_info.sample_rate = sample_rate;
+ config.offload_info.format = AUDIO_FORMAT_PCM_16_BIT;
+ break;
- rc = qahw_unload_module(qahw_mod_handle);
- if (rc) {
- printf("could not unload hal %d \n",rc);
- return -1;
- }
+ case FILE_MP3:
+ config.channel_mask = audio_channel_out_mask_from_count(channels);
+ config.offload_info.channel_mask = config.channel_mask;
+ config.sample_rate = sample_rate;
+ config.offload_info.sample_rate = sample_rate;
+ config.offload_info.format = AUDIO_FORMAT_MP3;
+ flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+ break;
- printf("Done with hal tests \n");
- return 0;
+ case FILE_AAC:
+ case FILE_AAC_ADTS:
+ config.channel_mask = audio_channel_out_mask_from_count(channels);
+ config.offload_info.channel_mask = config.channel_mask;
+ config.sample_rate = sample_rate;
+ config.offload_info.sample_rate = sample_rate;
+ if (!is_valid_aac_format_type(format_type)) {
+ fprintf(log_file, "Invalid format type for AAC %d\n", format_type);
+ goto EXIT;
+ }
+ config.offload_info.format = get_aac_format(filetype, format_type);
+ flags |= AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+ break;
+
+
+ default:
+ fprintf(stderr, "Does not support given filetype\n");
+ usage();
+ return 0;
+ }
+ config.offload_info.version = AUDIO_OFFLOAD_INFO_VERSION_CURRENT;
+ config.offload_info.size = sizeof(audio_offload_info_t);
+
+ fprintf(log_file, "Now playing to output_device=%d sample_rate=%d \n"
+ , output_device, config.offload_info.sample_rate);
+ const char* stream_name = "output_stream";
+
+ fprintf(log_file, "calling open_out_put_stream:\n");
+ rc = qahw_open_output_stream(qahw_mod_handle,
+ handle,
+ output_device,
+ flags,
+ &config,
+ &out_handle,
+ stream_name);
+ fprintf(log_file, "open output stream is sucess:%d out_handhle %p\n"
+ , rc, out_handle);
+ if (rc) {
+ fprintf(stdout, "could not open output stream %d \n", rc);
+ goto EXIT;
+ }
+
+ play_file(out_handle,
+ file_stream,
+ (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD));
+
+EXIT:
+
+ if (out_handle != nullptr) {
+ rc = qahw_out_standby(out_handle);
+ if (rc) {
+ fprintf(stdout, "out standby failed %d \n", rc);
+ }
+
+ rc = qahw_close_output_stream(out_handle);
+ if (rc) {
+ fprintf(stdout, "could not close output stream %d \n", rc);
+ }
+
+ rc = qahw_unload_module(qahw_mod_handle);
+ if (rc) {
+ fprintf(stdout, "could not unload hal %d \n", rc);
+ return -1;
+ }
+ }
+
+ if ((log_file != stdout) && (log_file != nullptr))
+ fclose(log_file);
+
+ if (file_stream != nullptr)
+ fclose(file_stream);
+
+ fprintf(stdout, "\nBYE BYE\n");
+ return 0;
}