android8.1中用libtinyalsa编写sound-hal驱动
前言
因笔者的项目中需要在android系统中实现虚拟声卡,实现android本地声音播放到远程终端、远程终端MIC录音送至android系统来;
验证过使用libmedia库实现AudioRecord和AudioTrack的方案,此方案由于音频路由策略和声音焦点问题,无法实现项目目标。
只能重构sound-hal层、通过libtinyalsa库直接控制声卡方式来实现此部分功能;因此就有了这篇记录文章,我们一起梳理如何实现。
因此hal层驱动不用向android层提供api接口,也不用实现sound-hal层所以接口。
首先、先贴上sound-hal层框架代码
int
virt_sound_adev_open(const hw_module_t* module, const char* name, hw_device_t** device)
{ALOGV(" %s (name=%s)", __func__, name);struct audio_device *adev;adev = (struct audio_device*) calloc(1, sizeof(struct audio_device));if (!adev)return -ENOMEM;adev->device.common.tag = HARDWARE_DEVICE_TAG;adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;adev->device.common.module = (struct hw_module_t *)module;adev->device.common.close = virt_sound_adev_close;adev->device.open_output_stream = adev_open_output_stream;adev->device.close_output_stream = adev_close_output_stream;adev->device.open_input_stream = adev_open_input_stream;adev->device.close_input_stream = adev_close_input_stream;*device = &adev->device.common;for(int i =0; i < OUTPUT_TOTAL; i++){adev->outputs[i] = NULL;}ALOGV(" %s() %d,device= 0x%x", __func__, __LINE__, *device);return 0;
}int virt_sound_adev_close(hw_device_t *device)
{ALOGV("%s() %d \n", __func__, __LINE__);free(device);return 0;
}
这个代码您是否似曾相识呢?这是sound-hal层的通用代码,都是通过构造 audio_device 对象,
来实现对声卡管理的.
打开音频输出流方法,如下:
int adev_open_output_stream(struct audio_hw_device *dev,audio_io_handle_t handle,audio_devices_t devices,audio_output_flags_t flags,struct audio_config *config,struct audio_stream_out **stream_out,const char *address __unused)
{int ret;struct audio_device *adev = (struct audio_device *)dev;struct stream_out *out;enum output_type type = OUTPUT_LOW_LATENCY;ALOGD("%s():%d adev=0x%x, dev=0x%x, stream_out=0x%x", __func__, __LINE__, adev, dev, *stream_out);out = (struct stream_out *)calloc(1, sizeof(struct stream_out));if (!out)return -ENOMEM;out->supported_channel_masks[0] = AUDIO_CHANNEL_OUT_STEREO;out->supported_channel_masks[1] = AUDIO_CHANNEL_OUT_MONO;if(config != NULL)memcpy(&(out->aud_config), config, sizeof(struct audio_config));out->channel_mask = AUDIO_CHANNEL_OUT_STEREO;out->flags = flags;out->output_direct = false;out->channel_buffer = NULL;out->bitstream_buffer = NULL;{//> author:ljbout->config = pcm_config; //> define at audio_hw.hppout->pcm_device = PCM_DEVICE;type = OUTPUT_LOW_LATENCY;out->output_direct = false;out->output_direct_mode = VIRTUAL_SOUND;out->config.format = PCM_FORMAT_S16_LE;}out->stream.write = out_write; //> 向声卡中写音频数据入口方法out->dev = adev;out->dev->pre_output_device_id = OUT_DEVICE_SPEAKER;out->dev->pre_input_source_id = IN_SOURCE_MIC;out->standby = true;out->nframes = 0;out->slice_time_up = 0;out->slice_time_down = 0;pthread_mutex_lock(&adev->lock_outputs);if (adev->outputs[type]) {pthread_mutex_unlock(&adev->lock_outputs);ret = -EBUSY;goto err_open;}adev->outputs[type] = out;pthread_mutex_unlock(&adev->lock_outputs);*stream_out = &out->stream;ALOGD(" %s():%d okay",__func__, __LINE__);return 0;err_open:free(out);*stream_out = NULL;ALOGW(" %s() %d dev open errors", __func__, __LINE__);return ret;
}void adev_close_output_stream(struct audio_hw_device *dev,struct audio_stream_out *stream)
{int type;struct stream_out *out = (struct stream_out *)&stream->common;struct audio_device *adev = out->dev;lock_all_outputs(adev);int i;if (!out->standby) {for (i = 0; i < PCM_TOTAL; i++) {if (out->pcm[i]) {pcm_close(out->pcm[i]);out->pcm[i] = NULL;}}out->standby = true;out->nframes = 0;unlock_all_outputs(adev, NULL);adev = (struct audio_device *)dev;pthread_mutex_lock(&adev->lock_outputs);for (type = 0; type < (int)OUTPUT_TOTAL; ++type) {if (adev->outputs[type] == (struct stream_out *) stream) {adev->outputs[type] = NULL;break;}}pthread_mutex_unlock(&adev->lock_outputs);free(stream);ALOGD(" %s() okay %d run over", __func__, __LINE__);
}
上面是输出流方法,就是audioTrack播放音频时、打开的音频输出流;当然我们编写此hal驱动
不向audioTrack模块开放,供我们自己封装使用(可以理解为构成播放器)。
音频输出流建立好,我们如何向声卡中送音频输入呢?
向声卡中送入音频数据
ssize_t out_write(struct audio_stream_out *stream, const void* buffer,size_t bytes)
{int ret = 0;int i;struct stream_out *out = (struct stream_out *)stream;struct audio_device *adev = out->dev;size_t newbytes = bytes * 2;out->out_data_size = bytes;out_dump(out, 0);pthread_mutex_lock(&out->lock);if (out->standby) {pthread_mutex_unlock(&out->lock);lock_all_outputs(adev);if (!out->standby) {unlock_all_outputs(adev, out);goto false_alarm;}out->pcm[PCM_CARD] = pcm_open(PCM_CARD, PCM_DEVICE_SCO, PCM_OUT | PCM_MONOTONIC, &out->config); ALOGD("%s()%d pcm_open(card=%d, device=%d,PCM_OUT,config.channels=0x%x) \n", __func__, __LINE__, PCM_CARD, PCM_DEVICE_SCO, out->config.channels);if (out->pcm[PCM_CARD] && !pcm_is_ready(out->pcm[PCM_CARD])) {ALOGE("pcm_open(PCM_CARD) failed: %s",pcm_get_error(out->pcm[PCM_CARD]));pcm_close(out->pcm[PCM_CARD]);unlock_all_outputs(adev, NULL);goto final_exit;}out->standby = false;unlock_all_outputs(adev, out);}
false_alarm:if (out->disabled) {ret = -EPIPE;goto exit;}ret = pcm_write(out->pcm[PCM_CARD], (void *)buffer, bytes);if (ret == 0) {out->written += bytes / (out->config.channels * sizeof(short));out->nframes = out->written;}
exit:pthread_mutex_unlock(&out->lock);
final_exit:if (ret != 0) {ALOGD("AudioData write error , keep slience! ret = %d", ret);usleep(bytes * 1000000 / audio_stream_out_frame_size(stream) /out_get_sample_rate(&stream->common));}ALOGD("%s(),%d written=%d, bytes=%d \n", __func__, __LINE__, out->written, bytes);return bytes;
}
此方法是向声卡中写数据具体实现,前面声卡设备和输出流只是构造内存中管理对象,
检测 out->standby 标识为如果没有打开声卡,就通过 pcm_open() 函数直接打开声卡,
打开后通过 pcm_write() 函数向声卡中写入音频数据,此时声卡就能够发声了。
声卡的关闭是在 adev_close_output_stream() 方法中实现的,sound-hal驱动
是不是也很简单呢=,不用向android用户空间提供api接口就这么简单;那我们如何使用
这个sound-hal呢?
sound-hal使用方法
下面是创建音频播放线程,在socket上读取音频数据、把音频数据写入声卡中。
#include
#include
#include #include
#include
#include #include
#include
#include
#include #include
#include #include "virtAudioHal/virt_sound_hal.h"
#include "virt_sound_output.h"#define LOG_TAG "virt_sound_output"
#define LOG_NDEBUG 0//> audio raw data sample rate = 44100, AUDIO_CHANNEL_OUT_STEREO, PCM_FORMAT_S16_LEstatic const audio_config_t VIRTUAL_SOUND_OUTPUT_INITIALIZER = {/* .sample_rate = */ 44100,/* .channel_mask = */ AUDIO_CHANNEL_OUT_STEREO,/* .format = */ (audio_format_t)PCM_FORMAT_S16_LE,/* .offload_info = */ {/* .version = */ AUDIO_OFFLOAD_INFO_VERSION_CURRENT,/* .size = */ sizeof(audio_offload_info_t),/* .sample_rate = */ 0,/* .channel_mask = */ 0,/* .format = */ AUDIO_FORMAT_DEFAULT,/* .stream_type = */ AUDIO_STREAM_VOICE_CALL,/* .bit_rate = */ 0,/* .duration_us = */ 0,/* .has_video = */ false,/* .is_streaming = */ false,/* .bit_width = */ 16,/* .offload_buffer_size = */ 0,/* .usage = */ AUDIO_USAGE_UNKNOWN},/* .frame_count = */ 4,
};static struct audio_device *g_Dev = NULL;
static struct stream_out* g_stream_out;
static int OUT_BUFFER_SIZE = 1024 *2;static int virt_snd_output_fd = -1;
static bool virt_snd_output_running = false;
static bool virt_snd_start_running = false;void exit_sound_output_process(){virt_snd_start_running = false;virt_snd_output_running = false;
}static void IPC_disconnect_handle_pipe(int signo){ALOGV("[%s: %d] pipe socket disconnect, sig:%d \n", __FILE__, __LINE__, signo);exit_sound_output_process();
}static void* sound_output_thread(void* argv)
{(void)argv;int readLen,writeLen;char empty = 'c';struct sigaction sa;sa.sa_handler = IPC_disconnect_handle_pipe;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sigaction(SIGPIPE, &sa, NULL);void * out_buffer = NULL;int ret = adev_open_output_stream((struct audio_hw_device*)g_Dev,0,0,0,(struct audio_config *)&VIRTUAL_SOUND_OUTPUT_INITIALIZER,&g_stream_out,&empty);if(ret < 0){ALOGE(" %s() open device failed, exit thread %d \n", __func__, __LINE__);return ((void*)0);}out_buffer = malloc(OUT_BUFFER_SIZE);if(out_buffer == NULL){ALOGE(" %s() malloc failed, exit thread %d \n", __func__, __LINE__);goto exit_thread;}ALOGD(" %s() : %d open output_stream success ", __func__, __LINE__);int count = 0;while(virt_snd_output_running){while(virt_snd_start_running){readLen = read(virt_snd_output_fd, out_buffer, OUT_BUFFER_SIZE);if(readLen <= 0){ALOGE("%s:%d read socket data ERROR,len = %d", __func__, __LINE__, readLen);exit_sound_output_process();break;}writeLen = out_write((struct audio_stream_out*)g_stream_out, out_buffer, readLen);if(writeLen < 0){ALOGE("%s:%d write pcm data ERROR,len = %d", __func__, __LINE__, writeLen);break;}ALOGD(" %s() pcm write bytes=%d, count= %d \n", __func__, writeLen, count++);}usleep(100000);}
exit_thread:adev_close_output_stream((struct audio_hw_device*)g_Dev, (struct audio_stream_out*)g_stream_out);close(virt_snd_output_fd);free(out_buffer);ALOGV(" %s():%d thread exited ... \n", __func__, __LINE__);return ((void*)0);
}int create_sound_output_thread(int c_socket, struct audio_device* dev)
{pthread_t id;if(dev == NULL || c_socket < 0){ALOGE(" %s() thread input parament ERROR %d \n", __func__, __LINE__);return -1;}if(virt_snd_output_running == false){virt_snd_output_running = true;virt_snd_start_running = true;virt_snd_output_fd = c_socket;g_Dev = dev;if(pthread_create(&id, NULL, sound_output_thread, NULL)!=0){ALOGE("native thread create fail");return -1;}ALOGD(" %s():%d created thread success \n", __func__, __LINE__);return 0;} else {ALOGD(" %s():%d Thread has exsited, please destroy it \n", __func__, __LINE__);return 1;}
}
此 create_sound_output_thread() 是创建音频输出线程接口,需要传递socket fd和audio_device*给线程,音频播放线程会从
socket中读取pcm数据、并写入声卡,音频格式 AUDIO_CHANNEL_OUT_STEREO、44100、PCM_FORMAT_S16_LE的源数据。
入口中 audio_device 如何创建呢?我们在看下面创建代码。
创建 audio_device 设备
下面代码是调用 virt_sound_adev_open() 来创建 audio_device 设备。
#include
#include #include
#include
#include #include
#include #include "virtAudioHal/virt_sound_hal.h"#include "virtual_sound_driver.h"
#include "virt_sound_output.h"
#include "virt_sound_input.h"#define LOG_TAG "virt_sound_driver"
#define LOG_NDEBUG 0static struct audio_device *g_Dev = NULL;
const static hw_module_t *module = NULL;static int socket_count = 0;
static int bufferSizeInBytes = 1024*4;
static int server_sockfd = -1;static bool g_bQuitThread = false;
static bool g_ThreadAction = true;static void IPC_disconnect_handle_pipe(int signo){ALOGW("[%s: %d] server socket pipe disconnected. sig:%d \n", __FILE__, __LINE__, signo);
}int main(int argc, char const *argv[])
{int count = 0;int rc = 0; int readLen = 0;void* inBuffer = NULL;int ret;int client_sockfd;struct sockaddr_un clientAddr;socklen_t len = sizeof(clientAddr);struct sigaction sa;sa.sa_handler = IPC_disconnect_handle_pipe;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sigaction(SIGPIPE, &sa, NULL);ret = virt_sound_adev_open(module, "mmm.virt.sound", &g_Dev);if(ret < 0){ALOGE(" %s():%d virt_sound_adev_open() failed ret=%d, exit thread \n", __func__, __LINE__, ret);return -1;}g_bQuitThread = false;ALOGD(" %s() %d wait for connecting... \n", __func__, __LINE__);while(!g_bQuitThread){server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);if(server_sockfd < 0){usleep(50000);ALOGV("[%s: %d] socket: %s create error !\n", __func__, __LINE__, SOCKETNAME);close(server_sockfd);continue;}struct sockaddr_un serverAddr;memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sun_family = AF_UNIX;strcpy(serverAddr.sun_path, SOCKETNAME);serverAddr.sun_path[0] = 0; //> abstract address first byte must be '0' .len = strlen(SOCKETNAME)+offsetof(struct sockaddr_un,sun_path);rc = bind(server_sockfd, (struct sockaddr*)&serverAddr, len);if(rc < 0){usleep(50000);ALOGW("[%s: %d] socket: %s bind error !\n", __func__, __LINE__, SOCKETNAME);close(server_sockfd);continue;}rc = listen(server_sockfd, 2);if(rc < 0){usleep(50000);ALOGW("[%s: %d] socket: %s listen error !\n", __func__, __LINE__, SOCKETNAME);close(server_sockfd);continue;}inBuffer = malloc(bufferSizeInBytes);if(inBuffer == NULL){ALOGE("[%s: %d] malloc size=%d failed \n", __func__, __LINE__, bufferSizeInBytes);break;}while(g_ThreadAction){memset(&clientAddr, 0 , sizeof(clientAddr));client_sockfd = accept(server_sockfd, (struct sockaddr*)&clientAddr, &len);if(client_sockfd < 0){usleep(50000);ALOGE("[%s: %d] socket: %s listen error !\n", __func__, __LINE__, SOCKETNAME);close(client_sockfd);continue;}fcntl(client_sockfd, F_SETFD, FD_CLOEXEC);ALOGD("[%s: %d] socket: '%d' connected VirtualSoundThread server !\n", __func__, __LINE__, client_sockfd);memset(inBuffer, 0, bufferSizeInBytes);readLen = read(client_sockfd, inBuffer, bufferSizeInBytes);if(readLen <= 0){ALOGE("Error:[%s: %d] read data stream error \n", __func__, __LINE__);break;} else {char *string = (char*)inBuffer;ALOGD("[%s: %d] read data='%s' , length: %d \n", __func__, __LINE__, string, readLen);rc = strcmp(string,OUT_AUTHORIZE); //> authorize client if(rc == 0){create_sound_output_thread(client_sockfd, (struct audio_device *)g_Dev);} else {rc = strcmp(string,IN_AUTHORIZE); //> authorize client if(rc == 0){create_sound_input_thread(client_sockfd, (struct audio_device *)g_Dev);} else {ALOGD("%s() : %d socket: '%d' authorize failed by closed!\n", __func__, __LINE__, client_sockfd);close(client_sockfd);}}}}free(inBuffer);close(client_sockfd);close(server_sockfd);ALOGV("[%s: %d] network disconnect, restart ... \n", __func__, __LINE__);}virt_sound_adev_close((hw_device_t*)g_Dev);ALOGV("[%s: %d] VirtualSoundThread exit ... \n", __func__, __LINE__);return 0;
}
void exit_virt_sound_service()
{g_bQuitThread = true;g_ThreadAction = false;exit_sound_input_thread();exit_sound_output_thread();
}
此service程序通过 virt_sound_adev_open() 创建音频设备,此设备生命周期与此service相同,至此我们就具有
audio-device来管理此声卡。
通过 socket(AF_UNIX, SOCK_STREAM, 0) 创建的UNIX SOCKET 套接字,来实现音频的输入与输出数据传输,
unix socket 不是本文介绍内容,看官如需了解此部分内容、请在我博客中搜索关键字。
当客户端链接到server-socket、并通过 OUT_AUTHORIZE 鉴权验证,service就创建 create_sound_output_thread() 输出流线程;
此输出流线程就向把网络上pcm数据源源不断的写入声卡中,也就实现播放器功能。
如果看到此处、恭喜你已经get到本篇文章重点了。我们通过sound-hal以及用例,系统的串通起来;聪明你可能会感觉sound-hal层驱动
设计逻辑与linux驱动框架如此相似呢。创新一直都不是凭空而来的。
上面内容作为框架认知够了,如果深入此部分内容的话,我们还需要了解接下来内容,您就可以写自己的sound-hal层程序了。
android系统sound结构相关的数据结构
@hardware/libhardware/include/hardware.h
typedef struct hw_module_methods_t {/** Open a specific device */int (*open)(const struct hw_module_t* module, const char* id,struct hw_device_t** device);} hw_module_methods_t;typedef struct hw_module_t {uint32_t tag;uint16_t module_api_version;
#define version_major module_api_versionuint16_t hal_api_version;
#define version_minor hal_api_versionconst char *id;const char *name;const char *author;struct hw_module_methods_t* methods;void* dso;#ifdef __LP64__uint64_t reserved[32-7];
#else/** padding to 128 bytes, reserved for future use */uint32_t reserved[32-7];
#endif} hw_module_t;typedef struct hw_device_t {uint32_t tag;uint32_t version;struct hw_module_t* module;/** padding reserved for future use */
#ifdef __LP64__uint64_t reserved[12];
#elseuint32_t reserved[12];
#endifint (*close)(struct hw_device_t* device);
} hw_device_t;
以上hw_module_methods_t、hw_module_t和hw_device_t是framework框架HAL的接口定义,
此部分我们只需了解,因我们不向android用户空间提供接口,不用实现。
sound config 相关
@system/media/audio/include/system/audio.h
typedef struct {uint16_t version; // version of the info structureuint16_t size; // total size of the structure including version and sizeuint32_t sample_rate; // sample rate in Hzaudio_channel_mask_t channel_mask; // channel maskaudio_format_t format; // audio formataudio_stream_type_t stream_type; // stream typeuint32_t bit_rate; // bit rate in bits per secondint64_t duration_us; // duration in microseconds, -1 if unknownbool has_video; // true if stream is tied to a video streambool is_streaming; // true if streaming, false if local playbackuint32_t bit_width;uint32_t offload_buffer_size; // offload fragment sizeaudio_usage_t usage;
} audio_offload_info_t;struct audio_config {uint32_t sample_rate;audio_channel_mask_t channel_mask;audio_format_t format;audio_offload_info_t offload_info;size_t frame_count;
};
typedef struct audio_config audio_config_t;
audio stream 相关
@hardware/libhardware/include/hardware/audio.h
struct audio_stream {/*** Put the audio hardware input/output into standby mode.* Driver should exit from standby mode at the next I/O operation.* Returns 0 on success and <0 on failure.*/int (*standby)(struct audio_stream *stream);/** dump the state of the audio input/output device */int (*dump)(const struct audio_stream *stream, int fd);/** Return the set of device(s) which this stream is connected to */audio_devices_t (*get_device)(const struct audio_stream *stream);/*** Currently unused - set_device() corresponds to set_parameters() with key* AUDIO_PARAMETER_STREAM_ROUTING for both input and output.* AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by* input streams only.*/int (*set_device)(struct audio_stream *stream, audio_devices_t device);
};
typedef struct audio_stream audio_stream_t;struct audio_stream_out {/*** Common methods of the audio stream out. This *must* be the first member of audio_stream_out* as users of this structure will cast a audio_stream to audio_stream_out pointer in contexts* where it's known the audio_stream references an audio_stream_out.*/struct audio_stream common;/*** Write audio buffer to driver. Returns number of bytes written, or a* negative status_t. If at least one frame was written successfully prior to the error,* it is suggested that the driver return that successful (short) byte count* and then return an error in the subsequent call.** If set_callback() has previously been called to enable non-blocking mode* the write() is not allowed to block. It must write only the number of* bytes that currently fit in the driver/hardware buffer and then return* this byte count. If this is less than the requested write size the* callback function must be called when more space is available in the* driver/hardware buffer.*/ssize_t (*write)(struct audio_stream_out *stream, const void* buffer,size_t bytes);/*** Notifies to the audio driver to stop playback however the queued buffers are* retained by the hardware. Useful for implementing pause/resume. Empty implementation* if not supported however should be implemented for hardware with non-trivial* latency. In the pause state audio hardware could still be using power. User may* consider calling suspend after a timeout.** Implementation of this function is mandatory for offloaded playback.*/int (*pause)(struct audio_stream_out* stream);/*** Notifies to the audio driver to flush the queued data. Stream must already* be paused before calling flush().** Implementation of this function is mandatory for offloaded playback.*/int (*flush)(struct audio_stream_out* stream);/*** Called by the framework to start a stream operating in mmap mode.* create_mmap_buffer must be called before calling start()** \note Function only implemented by streams operating in mmap mode.** \param[in] stream the stream object.* \return 0 in case of success.* -ENOSYS if called out of sequence or on non mmap stream*/int (*start)(const struct audio_stream_out* stream);/*** Called by the framework to stop a stream operating in mmap mode.* Must be called after start()** \note Function only implemented by streams operating in mmap mode.** \param[in] stream the stream object.* \return 0 in case of success.* -ENOSYS if called out of sequence or on non mmap stream*/int (*stop)(const struct audio_stream_out* stream);
};
typedef struct audio_stream_out audio_stream_out_t;struct audio_stream_in {/*** Common methods of the audio stream in. This *must* be the first member of audio_stream_in* as users of this structure will cast a audio_stream to audio_stream_in pointer in contexts* where it's known the audio_stream references an audio_stream_in.*/struct audio_stream common;/** Read audio buffer in from audio driver. Returns number of bytes read, or a* negative status_t. If at least one frame was read prior to the error,* read should return that byte count and then return an error in the subsequent call.*/ssize_t (*read)(struct audio_stream_in *stream, void* buffer,size_t bytes);/*** Called by the framework to start a stream operating in mmap mode.* create_mmap_buffer must be called before calling start()** \note Function only implemented by streams operating in mmap mode.** \param[in] stream the stream object.* \return 0 in case off success.* -ENOSYS if called out of sequence or on non mmap stream*/int (*start)(const struct audio_stream_in* stream);/*** Called by the framework to stop a stream operating in mmap mode.** \note Function only implemented by streams operating in mmap mode.** \param[in] stream the stream object.* \return 0 in case of success.* -ENOSYS if called out of sequence or on non mmap stream*/int (*stop)(const struct audio_stream_in* stream);};
typedef struct audio_stream_in audio_stream_in_t;struct audio_module {struct hw_module_t common;
};struct audio_hw_device {/*** Common methods of the audio device. This *must* be the first member of audio_hw_device* as users of this structure will cast a hw_device_t to audio_hw_device pointer in contexts* where it's known the hw_device_t references an audio_hw_device.*/struct hw_device_t common;};
typedef struct audio_hw_device audio_hw_device_t;
此sound-hal 定义的数据结构
@vendor/metis/virt_sound_hal/include/virtAudioHal/virt_sound_hw.h
struct audio_device {struct audio_hw_device device;pthread_mutex_t lock; /* see note below on mutex acquisition order */audio_devices_t out_device; /* "or" of stream_out.device for all active output streams */audio_devices_t in_device;struct pcm *pcm_voice_out;struct pcm *pcm_voice_in;struct stream_out *outputs[OUTPUT_TOTAL];pthread_mutex_t lock_outputs; /* see note below on mutex acquisition order */};struct stream_out {struct audio_stream_out stream;pthread_mutex_t lock; /* see note below on mutex acquisition order */struct pcm *pcm[PCM_TOTAL];struct pcm_config config;struct audio_config aud_config;unsigned int pcm_device;bool standby; /* true if all PCMs are inactive */bool disabled;audio_channel_mask_t channel_mask;/* Array of supported channel mask configurations. +1 so that the last entry is always 0 */audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1];bool muted;uint64_t written; /* total frames written, not cleared when entering standby */uint64_t nframes;bool output_direct;int slice_time_up;int slice_time_down;int out_data_size;struct audio_device *dev;struct resampler_itfe *resampler;};struct stream_in {struct audio_stream_in stream;pthread_mutex_t lock; /* see note below on mutex acquisition order */struct pcm *pcm;bool standby;unsigned int requested_rate;struct resampler_itfe *resampler;struct resampler_buffer_provider buf_provider;int16_t *buffer;size_t frames_in;int read_status;struct pcm_config *config;struct audio_device *dev;
};
上面是sound-hal例程中使用的、用户定义 stream_out 和 stream_in 的内容。
把service编译到android系统
下面贴处Android.mk 内容,供有需求的朋友参考。
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)
LOCAL_MODULE := virtual_sound_engine
LOCAL_MODULE_TAGS := optional#LOCAL_MULTILIB := both
#LOCAL_MODULE_PATH_32 := $(TARGET_OUT)/lib
#LOCAL_MODULE_PATH_64 := $(TARGET_OUT)/lib64LOCAL_SRC_FILES := \virt_sound_driver.c \virt_sound_output.c \virt_sound_input.c LOCAL_C_INCLUDES += \$(LOCAL_PATH) \$(KERNEL_HEADERS) \external/speex/include \external/tinyalsa/include \frameworks/av/include \frameworks/av/include/media \frameworks/native/include \\frameworks/av/services/audioflinger \frameworks/av/services/audiopolicy \frameworks/av/services/audiopolicy/common/managerdefinitions/include \frameworks/av/services/audiopolicy/common/include \frameworks/av/services/audiopolicy/engine/interface \frameworks/av/services/audiopolicy/service \frameworks/av/services/medialog \frameworks/av/services/soundtrigger \system/media/audio_route/include \vendor/metis/r_submix/include \LOCAL_SHARED_LIBRARIES:= \liblog libutils libcutils \libbinder \libmedia \libaudioutils \libaudiomanager \libaudioclient \libmediametrics \libvirt_sound_hal \LOCAL_CFLAGS += -g -DBUILD_FOR_ANDROIDLOCAL_LD_FLAGS += -nostartfilesLOCAL_PRELINK_MODULE := falseLOCAL_INIT_RC := VirtualSoundEngine.rc
$(info "LOCAL_MODULE = $(LOCAL_MODULE)")
include $(BUILD_EXECUTABLE)
include $(call all-makefiles-under,$(LOCAL_PATH))
把 sound-hal 编译到系统中 Android.bp 文件
// Copyright (C) 2012 The Android Open Source Project
//
// 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.cc_library_headers {name: "libvirt_sound_hw_headers",vendor_available: true,export_include_dirs: ["include"],
}cc_library_shared {name: "libvirt_sound_hw",srcs: ["virt_sound_hw.c"],include_dirs: ["external/speex/include","external/tinyalsa/include","frameworks/av/include/","frameworks/native/include/","frameworks/av/include/media","system/media/audio_route/include"],shared_libs: ["liblog","libcutils","libutils","libtinyalsa","libaudioutils","libhardware_legacy","libspeexresampler",],local_include_dirs: ["include"],export_include_dirs: ["include"],header_libs: ["libvirt_sound_hw_headers"],export_header_lib_headers: ["libvirt_sound_hw_headers"],static_libs: ["libmedia_helper","libspeex",],cflags: ["-Wno-unused-parameter","-Wno-error","-fPIC","-DANDROID",],
}
由于音频输入流与输出流类似,未贴出代码,有需求的可以私信我。
文章为做过多文字解释,关键流程和关系笔者已尽力描述,感谢大家阅读;此文对应如有启发或帮助,
您的点赞或关注,是笔者孜孜不倦的动力,感谢。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
