OpenSL ES 读取蓝牙语音遥控器音频

最近协助处理一个OpenSL的问题。问题背景和描述如下:

我司的安卓电视需要使用OpenSL从语音遥控器上采集音频,而语音遥控器有两种协议:2.4G和蓝牙。

在2.4G遥控上功能正常,但是使用蓝牙遥控器,没有任何报错,流程也正常跑通,但是读取到的音频数据全部是空数据。

其实OpenSL我之前也只是简单入门而已(笔记在这里),首先想到的是去修改它的设备类型

pHelper->device.locatorType = SL_DATALOCATOR_IODEVICE;
pHelper->device.deviceType = SL_IODEVICE_AUDIOINPUT;
pHelper->device.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;

但是查看了下locatorType、deviceType、deviceID可以选择的选项,发现并没有适合的:

/** Data locator */
#define SL_DATALOCATOR_URI          ((SLuint32) 0x00000001)
#define SL_DATALOCATOR_ADDRESS      ((SLuint32) 0x00000002)
#define SL_DATALOCATOR_IODEVICE     ((SLuint32) 0x00000003)
#define SL_DATALOCATOR_OUTPUTMIX        ((SLuint32) 0x00000004)
#define SL_DATALOCATOR_RESERVED5        ((SLuint32) 0x00000005)
#define SL_DATALOCATOR_BUFFERQUEUE  ((SLuint32) 0x00000006)
#define SL_DATALOCATOR_MIDIBUFFERQUEUE  ((SLuint32) 0x00000007)
#define SL_DATALOCATOR_RESERVED8        ((SLuint32) 0x00000008)

/** IODevice-types */
#define SL_IODEVICE_AUDIOINPUT  ((SLuint32) 0x00000001)
#define SL_IODEVICE_LEDARRAY    ((SLuint32) 0x00000002)
#define SL_IODEVICE_VIBRA       ((SLuint32) 0x00000003)
#define SL_IODEVICE_RESERVED4   ((SLuint32) 0x00000004)
#define SL_IODEVICE_RESERVED5   ((SLuint32) 0x00000005)

/** Device ids */
#define SL_DEFAULTDEVICEID_AUDIOINPUT   ((SLuint32) 0xFFFFFFFF)
#define SL_DEFAULTDEVICEID_AUDIOOUTPUT  ((SLuint32) 0xFFFFFFFE)
#define SL_DEFAULTDEVICEID_LED          ((SLuint32) 0xFFFFFFFD)
#define SL_DEFAULTDEVICEID_VIBRA        ((SLuint32) 0xFFFFFFFC)
#define SL_DEFAULTDEVICEID_RESERVED1    ((SLuint32) 0xFFFFFFFB)

其实以前在用java层的AudioRecorder的时候就遇到过获取不到蓝牙语音遥控器音频的问题,当时是通过将audioSource设置成MediaRecorder.AudioSource.CAMCORDER解决的.

所以猜测OpenSL里面大概也是这样处理,在OpenSLES_AndroidConfiguration.h下找到类似的宏定义:

/**   uses the microphone audio source with the same orientation as the camera
 *     if available, the main device microphone otherwise */
#define SL_ANDROID_RECORDING_PRESET_CAMCORDER           ((SLuint32) 0x00000002)

这个宏用于指定音频设备的预设值。也就是说需要改变的不是音频设备的类型,依然使用SL_DEFAULTDEVICEID_AUDIOINPUT这个设备去读取音频,只不过将它的配置改成SL_ANDROID_RECORDING_PRESET_CAMCORDER让这个AUDIOINPUT设备读取的是摄像机方向的麦克风数据。

那要怎么修改这个预设值呢?

安卓开发者文档找到了播放器的设置方法:

// CreateAudioPlayer and specify SL_IID_ANDROIDCONFIGURATION
// in the required interface ID array. Do not realize player yet.
// ...
SLAndroidConfigurationItf playerConfig;
result = (*playerObject)->GetInterface(playerObject,
    SL_IID_ANDROIDCONFIGURATION, &playerConfig);
assert(SL_RESULT_SUCCESS == result);
SLint32 streamType = SL_ANDROID_STREAM_ALARM;
result = (*playerConfig)->SetConfiguration(playerConfig,
    SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
assert(SL_RESULT_SUCCESS == result);
// ...
// Now realize the player here.

其实录音的方法也是类似的,通过SLAndroidConfigurationItf的SetConfiguration方法去配置。不过文档其实默认读者都是了解OpenSL的使用方法的了,所以还有些必要的代码其实没有展示出来。

要从playerObject里面用GetInterface获取到SLAndroidConfigurationItf,必须在CreateAudioPlayer的时候就将SLAndroidConfigurationItf的id传入,这样playerObject才会有SLAndroidConfigurationItf这个接口。要不然GetInterface会获取失败。

这里我就直接将录音代码展示出来了,注意看我注释的那两行:

SLObjectItf recorderObject;

SLInterfaceID id[] = { 
        SL_IID_ANDROIDSIMPLEBUFFERQUEUE, 
        SL_IID_ANDROIDCONFIGURATION     // 需要指定AudioRecorder包含SLAndroidConfigurationItf
};
SLboolean required[] = {
        SL_BOOLEAN_TRUE, 
        SL_BOOLEAN_TRUE  // 指定SL_IID_ANDROIDCONFIGURATION是必须包含的
};

(*engineInterface)->CreateAudioRecorder(
        engineInterface,
        &(recorderObject),
        &(source),
        &(sink),
        2,
        id,
        required
);

SLAndroidConfigurationItf configItf;
(*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDCONFIGURATION, (void*)&configItf);

SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_CAMCORDER;
(*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, &presetValue, sizeof(SLuint32));

(*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);

顺嘴说一句,如果我们不设置SL_ANDROID_RECORDING_PRESET_CAMCORDER,它默认是用SL_ANDROID_RECORDING_PRESET_GENERIC。

所有的配置如下:


/**  配置中“无”不能设置,它是用来表示当前设置不匹配任何配置 */
#define SL_ANDROID_RECORDING_PRESET_NONE                ((SLuint32) 0x00000000)

/**  平台上通用的录像配置 */
#define SL_ANDROID_RECORDING_PRESET_GENERIC             ((SLuint32) 0x00000001)

/**  如果有的话使用具有相同的方向与摄像机的麦克风的音频源,否则使用过主麦克风 */
#define SL_ANDROID_RECORDING_PRESET_CAMCORDER           ((SLuint32) 0x00000002)

/**  使用为语音识别优化过的主要麦克风 */
#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION   ((SLuint32) 0x00000003)

/** 使用为音频通信优化过的主要麦克风 */
#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)

/** 使用未经处理的主麦克风 */
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED         ((SLuint32) 0x00000005)

其它坑

然后除了上面的SL_IID_ANDROIDCONFIGURATION之外,其实我这边还遇到了几个坑,这里也记录一下

  • 采样率需要和录音设备硬件一致

由于代码里面一直用16000Hz的采样率,在2.4G语音遥控器上也工作的好好的,于是适配蓝牙语音遥控之后就没有修改它,然后发现读取直接阻塞住了,读取音频数据的回调一直没有被调用(如果不设置SL_ANDROID_RECORDING_PRESET_CAMCORDER的时候虽然也拿不到音频数据,但是回调是会调用的,只不过拿到的数据都是空数据)。之后改成了和硬件一致的44100Hz之后就正常了

  • SetConfiguration必须在Realize前面调用

其实这块并不是我司代码的问题,我们的项目接入了亚马逊的Alexa sdk,它对OpenSL进行了一层封装,它在创建AudioRecorder之后顺手就Realize了,但是它原本也是有提供封装好的SetConfiguration的配置的,于是我们就直接在上面改了。

于是就一直没有效果。最后还是在分析log的时候发现调用SetConfiguration的时候底层输出了“Realize已经调用”的打印才找到问题所在。

这里忍不住吐槽一下,亚马逊写这块的人明显对OpenSL也不是很熟悉,除了这个问题之外我们还发现了不少其他的低级问题。看来亚马逊这种大公司的代码审查也做的不怎么样嘛......

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容