691 lines
22 KiB
C
691 lines
22 KiB
C
/*
|
|
* Copyright (C) 2017 Amlogic Corporation.
|
|
*
|
|
* Licensed 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.
|
|
*/
|
|
|
|
#define LOG_TAG "audio_hw_hal_manager"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include "aml_audio_log.h"
|
|
#include <system/audio.h>
|
|
#include <hardware/audio.h>
|
|
#include <sound/asound.h>
|
|
#include <tinyalsa/asoundlib.h>
|
|
#include <inttypes.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
|
|
#include "audio_hw.h"
|
|
#include "alsa_manager.h"
|
|
#include "audio_hw_utils.h"
|
|
#include "alsa_device_parser.h"
|
|
#include "dolby_lib_api.h"
|
|
#include "aml_audio_stream.h"
|
|
#include "alsa_config_parameters.h"
|
|
#include "dtv_patch.h"
|
|
#include "aml_audio_timer.h"
|
|
#include "dtv_private_object.h"
|
|
#include "aml_config_data.h"
|
|
#include "aml_dump_debug.h"
|
|
#include "tv_patch_ctrl.h"
|
|
|
|
#define AML_ZERO_ADD_MIN_SIZE 1024
|
|
|
|
#define AUDIO_EAC3_FRAME_SIZE 16
|
|
#define AUDIO_AC3_FRAME_SIZE 4
|
|
#define AUDIO_TV_PCM_FRAME_SIZE 32
|
|
#define AUDIO_DEFAULT_PCM_FRAME_SIZE 4
|
|
|
|
#define MAX_AVSYNC_GAP (10*90000)
|
|
#define MAX_AVSYNC_WAIT_TIME (3*1000*1000)
|
|
|
|
#define ALSA_DELAY_THRESHOLD_MS_NORMAL (32)
|
|
#define ALSA_DELAY_THRESHOLD_MS_LLP (16)
|
|
|
|
#define ALSA_OUTPUT_PCM_FILE "alsa_pcm_write.raw"
|
|
#define ALSA_OUTPUT_SPDIF_FILE "alsa_spdif_write"
|
|
|
|
#define WAIT_COUNT_MAX 30
|
|
|
|
#define ALSA_DELAY_THRESHOLD_MS ((alsa_llp_mode) ? ALSA_DELAY_THRESHOLD_MS_LLP : ALSA_DELAY_THRESHOLD_MS_NORMAL)
|
|
|
|
static bool alsa_llp_mode;
|
|
|
|
typedef struct alsa_handle {
|
|
unsigned int card;
|
|
unsigned int pcm_index; /*used for open the alsa device*/
|
|
struct pcm_config config;
|
|
struct pcm *pcm;
|
|
int block_mode;
|
|
unsigned int alsa_port; /*refer to PORT_SPDIF, PORT***/
|
|
audio_format_t format;
|
|
uint32_t start_threshold;
|
|
uint32_t write_cnt;
|
|
uint64_t write_frames;
|
|
bool alsa_running_status;// for pcm output, av sync
|
|
bool alsa_status_changed;// for pcm output, av sync
|
|
} alsa_handle_t;
|
|
|
|
static int aml_audio_get_alsa_debug() {
|
|
return get_debug_value(AML_DEBUG_AUDIOHAL_ALSA);
|
|
}
|
|
|
|
static int aml_alsa_pcm_close(alsa_handle_t *handle)
|
|
{
|
|
struct pcm *pcm = NULL;
|
|
if (handle == NULL || handle->pcm == NULL) {
|
|
ALOGE("%s handle is NULL\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
pcm = handle->pcm;
|
|
pcm_close(pcm);
|
|
aml_audio_free(handle);
|
|
return 0;
|
|
}
|
|
void aml_alsa_set_llp(bool mode)
|
|
{
|
|
alsa_llp_mode = mode;
|
|
}
|
|
|
|
int aml_alsa_input_open(void **handle, aml_alsa_config_t * stream_config, aml_device_config_t *device_config)
|
|
{
|
|
alsa_handle_t * alsa_handle = NULL;
|
|
struct pcm_config *config = NULL;
|
|
int ret = 0;
|
|
alsa_handle = (alsa_handle_t *)aml_audio_calloc(1, sizeof(alsa_handle_t));
|
|
if (alsa_handle == NULL) {
|
|
AM_LOGE("malloc alsa_handle failed\n");
|
|
return -1;
|
|
}
|
|
config = &alsa_handle->config;
|
|
alsa_handle->card = alsa_device_get_card_index();
|
|
alsa_handle->alsa_port = device_config->device_port;
|
|
if (alsa_handle->alsa_port == PORT_BUILTINMIC) {
|
|
alsa_handle->pcm_index = aml_get_json_int_value("Builtinmic_Alsa_Dev_Id", alsa_device_update_pcm_index(PORT_BUILTINMIC, CAPTURE));
|
|
} else {
|
|
alsa_handle->pcm_index = alsa_device_update_pcm_index(alsa_handle->alsa_port, CAPTURE);
|
|
}
|
|
config->format = convert_audio_format_2_alsa_format(stream_config->format);
|
|
config->channels = audio_channel_count_from_in_mask(stream_config->channel_mask);
|
|
config->rate = stream_config->sample_rate;
|
|
config->period_size = stream_config->period_size;
|
|
config->period_count = stream_config->period_count;
|
|
config->stop_threshold = config->period_size * config->period_count;
|
|
config->silence_size = LONG_MAX;
|
|
|
|
AM_LOGI("open card(%d), device(%d)", alsa_handle->card, alsa_handle->pcm_index);
|
|
AM_LOGI("ALSA open configs: channels %d format %d period_count %d period_size %d rate %d",
|
|
config->channels, config->format, config->period_count, config->period_size, config->rate);
|
|
AM_LOGI("ALSA open configs: threshold start %lu stop %lu silence %lu silence_size %lu avail_min %lu",
|
|
config->start_threshold, config->stop_threshold, config->silence_threshold, config->silence_size, config->avail_min);
|
|
/* non-blocking read prevent 10s stuck when switching TV sources */
|
|
alsa_handle->pcm = pcm_open(alsa_handle->card, alsa_handle->pcm_index, PCM_IN | PCM_NONBLOCK, config);
|
|
if (!alsa_handle->pcm || !pcm_is_ready(alsa_handle->pcm)) {
|
|
AM_LOGE("open device failed: %s", pcm_get_error(alsa_handle->pcm));
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
*handle = (void *)alsa_handle;
|
|
error:
|
|
if (ret < 0 && alsa_handle) {
|
|
aml_alsa_pcm_close(alsa_handle);
|
|
*handle = NULL;
|
|
}
|
|
AM_LOGI("alsa_input_open handle %p, ret %d", *handle, ret);
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_input_read(void *handle, void *buffer, int bytes) {
|
|
|
|
int ret = 0;
|
|
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
if (alsa_handle == NULL || alsa_handle->pcm == NULL) {
|
|
AM_LOGE("alsa_handle or pcm is NULL!!!");
|
|
return -1;
|
|
}
|
|
|
|
ret = pcm_read(alsa_handle->pcm, (unsigned char *)buffer, bytes);
|
|
|
|
if (ret < 0) {
|
|
if (ret != -1) {
|
|
AM_LOGE("pcm_read fail, ret:%d, error info:%s", ret, strerror(errno));
|
|
memset(buffer, 0, bytes);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int aml_alsa_input_flush(void *handle)
|
|
{
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
if (alsa_handle == NULL || alsa_handle->pcm == NULL) {
|
|
AM_LOGE("alsa_handle or pcm is NULL!!!");
|
|
return -1;
|
|
}
|
|
|
|
int ret = pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_RESET, 0);
|
|
if (ret < 0) {
|
|
AM_LOGE("cannot reset pcm!");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_input_get_delay(void *handle) {
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
int ret = 0;
|
|
if (alsa_handle == NULL || alsa_handle->pcm == NULL) {
|
|
AM_LOGE("alsa_handle or pcm is NULL!!!");
|
|
return 0;
|
|
}
|
|
|
|
struct pcm *pcm = alsa_handle->pcm;
|
|
snd_pcm_sframes_t delay = 0;
|
|
int delay_frames = 0;
|
|
|
|
ret = pcm_ioctl(pcm, SNDRV_PCM_IOCTL_DELAY, &delay);
|
|
|
|
if (ret < 0) {
|
|
AM_LOGE("cannot get delay!");
|
|
} else {
|
|
delay_frames = delay;
|
|
}
|
|
|
|
return delay_frames;
|
|
}
|
|
|
|
uint64_t aml_alsa_input_get_timestamp(void *handle) {
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
if (alsa_handle == NULL || alsa_handle->pcm == NULL) {
|
|
AM_LOGE("alsa_handle or pcm is NULL!!!");
|
|
return 0;
|
|
}
|
|
|
|
return pcm_get_timestamp(alsa_handle->pcm, alsa_handle->config.rate, 0);
|
|
}
|
|
|
|
int aml_alsa_input_stop(void *handle)
|
|
{
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
if (alsa_handle == NULL || alsa_handle->pcm == NULL) {
|
|
AM_LOGE("alsa_handle or pcm is NULL!!!");
|
|
return -1;
|
|
}
|
|
|
|
int ret = pcm_stop(alsa_handle->pcm);
|
|
if (ret < 0) {
|
|
AM_LOGE("cannot stop pcm!");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_input_close(void *handle)
|
|
{
|
|
AM_LOGI("handle %p", handle);
|
|
return aml_alsa_pcm_close((alsa_handle_t *)handle);
|
|
}
|
|
|
|
int aml_alsa_output_open(void **handle, aml_alsa_config_t * stream_config, aml_device_config_t *device_config)
|
|
{
|
|
struct pcm_config *config = NULL;
|
|
int card = 0;
|
|
int alsa_port = 0;
|
|
int pcm_index = -1;
|
|
struct pcm *pcm = NULL;
|
|
alsa_handle_t * alsa_handle = NULL;
|
|
bool platform_is_tv = 0;
|
|
audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
|
|
unsigned int channels = 0;
|
|
unsigned int rate = 0;
|
|
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
|
|
|
|
|
|
alsa_handle = (alsa_handle_t *)aml_audio_calloc(1, sizeof(alsa_handle_t));
|
|
if (alsa_handle == NULL) {
|
|
AM_LOGE("malloc alsa_handle failed\n");
|
|
return -1;
|
|
}
|
|
|
|
config = &alsa_handle->config;
|
|
format = stream_config->format;
|
|
channels = audio_channel_count_from_out_mask(stream_config->channel_mask);
|
|
rate = stream_config->sample_rate;
|
|
|
|
if (audio_is_linear_pcm(format)) {
|
|
get_hardware_config_parameters(config, format, adev->default_alsa_ch, rate, platform_is_tv,
|
|
stream_config->format == AUDIO_FORMAT_IEC61937, is_low_latency_mode(adev));
|
|
} else {
|
|
get_hardware_config_parameters(config, format, channels, rate, platform_is_tv,
|
|
stream_config->format == AUDIO_FORMAT_IEC61937, is_low_latency_mode(adev));
|
|
}
|
|
|
|
config->channels = channels;
|
|
config->rate = rate;
|
|
|
|
if (config->rate == 0 || config->channels == 0) {
|
|
|
|
AM_LOGE("Invalid sampleate=%d channel=%d\n", config->rate, config->channels);
|
|
goto exit;
|
|
}
|
|
|
|
if (format == AUDIO_FORMAT_PCM_16_BIT) {
|
|
config->format = PCM_FORMAT_S16_LE;
|
|
} else if (format == AUDIO_FORMAT_PCM_32_BIT) {
|
|
config->format = PCM_FORMAT_S32_LE;
|
|
} else {
|
|
config->format = PCM_FORMAT_S16_LE;
|
|
}
|
|
|
|
config->avail_min = 0;
|
|
|
|
card = alsa_device_get_card_index();
|
|
|
|
alsa_port = device_config->device_port;
|
|
|
|
if (alsa_port < 0) {
|
|
AM_LOGE("Wrong alsa_device ID\n");
|
|
goto exit;
|
|
}
|
|
pcm_index = alsa_device_update_pcm_index(alsa_port, PLAYBACK);
|
|
if (pcm_index < 0) {
|
|
int config_pcm_index;
|
|
config_pcm_index = aml_get_json_int_value("HDMITX_HBR_PCM_INDEX", -1);
|
|
if (config_pcm_index == 2) {
|
|
pcm_index = config_pcm_index;
|
|
config->period_size = 2048;
|
|
config->start_threshold = 0;
|
|
}
|
|
}
|
|
config->silence_size = LONG_MAX;
|
|
ALOGI("In pcm open ch=%d rate=%d\n", config->channels, config->rate);
|
|
ALOGI("%s, audio open card(%d), device(%d) \n", __func__, card, pcm_index);
|
|
ALOGI("ALSA open configs: channels %d format %d period_count %d period_size %d rate %d \n",
|
|
config->channels, config->format, config->period_count, config->period_size, config->rate);
|
|
ALOGI("ALSA open configs: threshold start %lu stop %lu silence %lu silence_size %lu avail_min %lu \n",
|
|
config->start_threshold, config->stop_threshold, config->silence_threshold, config->silence_size, config->avail_min);
|
|
|
|
pcm = pcm_open(card, pcm_index, PCM_OUT | PCM_MONOTONIC, config);
|
|
if (!pcm || !pcm_is_ready(pcm)) {
|
|
ALOGE("%s, pcm %p open [ready %d] failed. error info:%s\n", __func__, pcm, pcm_is_ready(pcm), pcm_get_error(pcm));
|
|
pcm_close(pcm);
|
|
goto exit;
|
|
}
|
|
|
|
alsa_handle->card = card;
|
|
alsa_handle->pcm_index = pcm_index;
|
|
alsa_handle->pcm = pcm;
|
|
alsa_handle->alsa_port = alsa_port;
|
|
alsa_handle->format = format;
|
|
alsa_handle->write_cnt = 0;
|
|
alsa_handle->write_frames = 0;
|
|
alsa_handle->start_threshold = config->start_threshold;
|
|
|
|
*handle = (void*)alsa_handle;
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
if (alsa_handle) {
|
|
aml_audio_free(alsa_handle);
|
|
}
|
|
*handle = NULL;
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
int aml_alsa_output_close(void *handle) {
|
|
AM_LOGI("handle %p", handle);
|
|
return aml_alsa_pcm_close((alsa_handle_t *)handle);
|
|
}
|
|
|
|
int aml_alsa_output_write(void *handle, const void *buffer, int bytes) {
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = NULL;
|
|
alsa_handle = (alsa_handle_t *)handle;
|
|
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
|
|
int debug_enable = aml_audio_get_alsa_debug();
|
|
if (alsa_handle == NULL || alsa_handle->pcm == NULL || buffer == NULL || bytes == 0) {
|
|
//AM_LOGE("invalid param, alsa_handle:%p, pcm:%p, buffer:%p, bytes:%zu",
|
|
//alsa_handle, alsa_handle?alsa_handle->pcm:0, buffer, bytes);
|
|
return -1;
|
|
}
|
|
#if 0
|
|
//ALOGD("handle=%p pcm=%p\n",alsa_handle,alsa_handle->pcm);
|
|
/*add for work around ddp dd ouput ,ddp underrun issue */
|
|
dd_pcm = alsa_handle->pcm;
|
|
ddp_pcm = adev->pcm_handle[adev->ms12.device];
|
|
if (is_sc2_chip() && eDolbyMS12Lib == adev->dolby_lib_type && dd_pcm && ddp_pcm && (alsa_handle->format != AUDIO_FORMAT_MAT)) {
|
|
snd_pcm_sframes_t delay_ddp = 0,delay_dd = 0;
|
|
|
|
ret = pcm_ioctl(dd_pcm, SNDRV_PCM_IOCTL_DELAY, &delay_dd);
|
|
if (ret < 0) {
|
|
delay_dd = alsa_handle->config.start_threshold;
|
|
}
|
|
ret = pcm_ioctl(ddp_pcm, SNDRV_PCM_IOCTL_DELAY, &delay_ddp);
|
|
if (ret < 0) {
|
|
delay_ddp = adev->ms12_config.start_threshold;
|
|
}
|
|
|
|
if (delay_dd + write_frames >= alsa_handle->config.start_threshold * 2)
|
|
overflow_flag = 1;
|
|
/* dd write blocked and ddp delay at a low level ,so skip dd data to avoid ddp underun*/
|
|
if (overflow_flag && delay_ddp <= 2 * 6144)
|
|
underrun_flag = 1;
|
|
|
|
if ( underrun_flag ) {
|
|
ALOGI("skip dd data for delay_dd frame =%ld delay_ddp frame %ld\n",delay_dd, delay_ddp);
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
struct snd_pcm_status status;
|
|
bool alsa_status;
|
|
pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_STATUS, &status);
|
|
if (PORT_I2S == alsa_handle->alsa_port) {// for pcm output
|
|
alsa_status = (status.state == PCM_STATE_RUNNING);
|
|
if (alsa_status != alsa_handle->alsa_running_status) {
|
|
ALOGI("alsa_running_status change from %d to %d", alsa_handle->alsa_running_status, alsa_status);
|
|
alsa_handle->alsa_running_status = alsa_status;
|
|
alsa_handle->alsa_status_changed = true;
|
|
}
|
|
}
|
|
|
|
if (status.state != PCM_STATE_RUNNING) {
|
|
AM_LOGW("format =0x%x alsa underrun(%d), state(%d)", alsa_handle->format, status.state==PCM_STATE_XRUN, status.state);
|
|
}
|
|
}
|
|
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT)) {
|
|
char file_name[128] = { 0 };
|
|
if (alsa_handle->format == AUDIO_FORMAT_AC3) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "dd");
|
|
} else if (alsa_handle->format == AUDIO_FORMAT_E_AC3) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "ddp");
|
|
} else if (alsa_handle->format == AUDIO_FORMAT_MAT) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "mat");
|
|
} else if (alsa_handle->format == AUDIO_FORMAT_DTS) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "dts");
|
|
} else if (alsa_handle->format == AUDIO_FORMAT_DTS_HD) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "dtshd");
|
|
} else if(alsa_handle->format == AUDIO_FORMAT_DTS_HD_MA) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "dtshd_ma");
|
|
} else if(alsa_handle->format == AUDIO_FORMAT_MPEGH) {
|
|
snprintf(file_name, 128, "%s.%s", ALSA_OUTPUT_SPDIF_FILE, "mpegh");
|
|
} else {
|
|
snprintf(file_name, 128, "%s_%dch.%s", ALSA_OUTPUT_SPDIF_FILE, alsa_handle->config.channels, "pcm");
|
|
}
|
|
aml_dump_audio_bitstreams(file_name, buffer, bytes);
|
|
}
|
|
|
|
if (get_debug_value(AML_DEBUG_AUDIOHAL_LEVEL_DETECT) && (AUDIO_FORMAT_PCM_16_BIT == alsa_handle->format)) {
|
|
check_audio_level("alsa_pcm", buffer, bytes);
|
|
}
|
|
|
|
alsa_handle->write_cnt++;
|
|
alsa_handle->write_frames += pcm_bytes_to_frames(alsa_handle->pcm, bytes);
|
|
|
|
if (debug_enable || (alsa_handle->write_cnt % 1000) == 0 || get_debug_value(AML_DEBUG_AUDIOHAL_GAME_MODE)) {
|
|
snd_pcm_sframes_t frames = 0;
|
|
ret = pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_DELAY, &frames);
|
|
ALOGI("alsa format =0x%x delay frames =%ld total frames=%"PRIu64" write_bytes=%d write_frame=%d", alsa_handle->format, frames, alsa_handle->write_frames, bytes, pcm_bytes_to_frames(alsa_handle->pcm, bytes));
|
|
}
|
|
|
|
if (adev->reset_hdmitx_audio && adev->active_outport == OUTPORT_HDMI &&((alsa_handle->format == adev->sink_format) || (alsa_handle->format == AUDIO_FORMAT_E_AC3))) {
|
|
pcm_stop(alsa_handle->pcm);
|
|
adev->reset_hdmitx_audio = false;
|
|
AM_LOGI("reset hdmitx alsa, output format=%x, sink format=%x", alsa_handle->format, adev->sink_format);
|
|
}
|
|
|
|
ret = pcm_write(alsa_handle->pcm, buffer, bytes);
|
|
if (ret < 0) {
|
|
AM_LOGE("write failed,pcm handle %p %s,",
|
|
alsa_handle->pcm, pcm_get_error(alsa_handle->pcm));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_output_getinfo(void *handle, alsa_info_type_t type, alsa_output_info_t * info) {
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = NULL;
|
|
alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return -1;
|
|
}
|
|
switch (type) {
|
|
case OUTPUT_INFO_DELAYFRAME: {
|
|
snd_pcm_sframes_t delay = 0;
|
|
int rate = alsa_handle->config.rate;
|
|
int rate_multiply = 1;
|
|
ret = pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_DELAY, &delay);
|
|
if (ret < 0) {
|
|
info->delay_ms = 0;
|
|
return -1;
|
|
}
|
|
if (alsa_handle->format == AUDIO_FORMAT_MAT) {
|
|
rate_multiply = 4;
|
|
} else if (alsa_handle->format == AUDIO_FORMAT_E_AC3) {
|
|
rate_multiply = 4;
|
|
}
|
|
info->delay_ms = delay * 1000 / (rate * rate_multiply);
|
|
return 0;
|
|
}
|
|
default:
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void *get_internal_pcm(void *handle) {
|
|
if (!handle) {
|
|
return NULL;
|
|
}
|
|
return ((alsa_handle_t *)handle)->pcm;
|
|
}
|
|
|
|
int aml_alsa_output_pause(void *handle) {
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return -1;
|
|
}
|
|
if (alsa_handle->pcm) {
|
|
ret = pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_PAUSE, 1);
|
|
if (ret < 0) {
|
|
ALOGE("%s error %d", __func__, ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_output_resume(void *handle) {
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return -1;
|
|
}
|
|
if (alsa_handle->pcm) {
|
|
ret = pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_PREPARE);
|
|
if (ret < 0) {
|
|
ALOGE("%s error %d", __func__, ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_output_stop(void *handle) {
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return -1;
|
|
}
|
|
if (alsa_handle->pcm) {
|
|
ret = pcm_stop(alsa_handle->pcm);
|
|
if (ret < 0) {
|
|
AM_LOGE("stop pcm failed,pcm handle %p %s",
|
|
alsa_handle->pcm, pcm_get_error(alsa_handle->pcm));
|
|
}
|
|
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool aml_alsa_output_get_alsa_status(void *handle, bool *alsa_status) {
|
|
bool ret = false;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return ret;
|
|
}
|
|
|
|
ret = alsa_handle->alsa_status_changed;
|
|
*alsa_status = alsa_handle->alsa_running_status;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_output_clear_alsa_status_changed_flag(void *handle) {
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
alsa_handle->alsa_status_changed = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ignore_default == true, means that if we can not get delay frames from alsa, we will return 0 frame
|
|
// ignore_default == false, means that if we can not get delay frames from alsa, we will return start threshold
|
|
uint64_t aml_alsa_output_get_delay_frame(void *handle, bool ignore_default)
|
|
{
|
|
uint64_t delay_frames = 0;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
int ret = -1;
|
|
snd_pcm_sframes_t frames = 0;
|
|
|
|
if (alsa_handle) {
|
|
if (alsa_handle->pcm && pcm_is_ready(alsa_handle->pcm)) {
|
|
ret = pcm_ioctl(alsa_handle->pcm, SNDRV_PCM_IOCTL_DELAY, &frames);
|
|
if (ret < 0) {
|
|
AM_LOGE("pcm_ioctl fail, ret:%#x, error info:%s",
|
|
ret, strerror(errno));
|
|
} else if (ignore_default && !alsa_handle->alsa_running_status) {
|
|
delay_frames = 0;
|
|
} else if (frames >= 0) {
|
|
delay_frames = frames;
|
|
}
|
|
}
|
|
|
|
if (ret < 0) {
|
|
if (ignore_default) {
|
|
delay_frames = 0;
|
|
} else {
|
|
delay_frames = alsa_handle->config.start_threshold;
|
|
}
|
|
}
|
|
}
|
|
return delay_frames;
|
|
}
|
|
|
|
bool aml_alsa_output_get_start_threshold(void *handle, uint32_t *start_threshold)
|
|
{
|
|
bool ret = false;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
if (alsa_handle && start_threshold) {
|
|
*start_threshold = alsa_handle->start_threshold;
|
|
ret = true;
|
|
} else {
|
|
AM_LOGE("Parameter Error: alsa_handle(%p), start_threshold(%p)", alsa_handle, start_threshold);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_output_flush(void *handle)
|
|
{
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (handle == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (alsa_handle->pcm && pcm_is_ready (alsa_handle->pcm)) {
|
|
ret = pcm_ioctl (alsa_handle->pcm, SNDRV_PCM_IOCTL_RESET, 1);
|
|
if (ret < 0) {
|
|
AM_LOGE("cannot reset alsa\n");
|
|
} else {
|
|
AM_LOGI("reset alsa success\n");
|
|
}
|
|
} else {
|
|
if (alsa_handle->pcm) {
|
|
AM_LOGE("pcm is not ready(%d)\n", pcm_is_ready(alsa_handle->pcm));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_alsa_output_get_timestamp(void *handle, unsigned int *avail, struct timespec *tstamp)
|
|
{
|
|
int ret = -1;
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
|
|
if (alsa_handle && alsa_handle->pcm) {
|
|
ret = pcm_get_htimestamp(alsa_handle->pcm, avail, tstamp);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum pcm_format convert_audio_format_2_alsa_format(audio_format_t format)
|
|
{
|
|
switch (format) {
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
return PCM_FORMAT_S16_LE;
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
return PCM_FORMAT_S32_LE;
|
|
case AUDIO_FORMAT_PCM_8_BIT:
|
|
return PCM_FORMAT_S8;
|
|
case AUDIO_FORMAT_PCM_8_24_BIT:
|
|
return PCM_FORMAT_S24_LE;
|
|
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
|
|
return PCM_FORMAT_S24_3LE;
|
|
default:
|
|
AM_LOGE("invalid format:%#x, return 16bit format.", format);
|
|
return PCM_FORMAT_S16_LE;
|
|
}
|
|
}
|
|
|
|
void aml_alsa_info_dump(void* handle, int fd)
|
|
{
|
|
alsa_handle_t * alsa_handle = (alsa_handle_t *)handle;
|
|
dprintf(fd, "\t\tcard:%d\n", alsa_handle->card);
|
|
dprintf(fd, "\t\tpcm_index:%d\n", alsa_handle->pcm_index);
|
|
dprintf(fd, "\t\talsa_port:%d\n", alsa_handle->alsa_port);
|
|
dprintf(fd, "\t\tformat:%x\n", alsa_handle->format);
|
|
}
|