移动端Android推流

本文介绍如何使用移动端Android SDK来支持实时记录场景下的音频识别流程。

前提条件

SDK关键接口

  • initialize:初始化SDK。

    /**
     * 初始化SDK,SDK为单例,请先释放后再次进行初始化。请勿在UI线程调用,可能会引起阻塞。
     * @param callback:事件监听回调,参见下文具体回调。
     * @param parameters:json string形式的初始化参数,参见如下说明。
     * @param level:log打印级别,值越小打印越多。
     * @param save_log:是否保存log为文件,存储目录为ticket中的debug_path字段值。注意,log文件无上限,请注意持续存储导致磁盘存满。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public synchronized int initialize(final INativeNuiCallback callback,
                                       String parameters,
                                       final Constants.LogLevel level,
                                       final boolean save_log)

    其中,parameters详细说明:

    参数

    类型

    是否必选

    说明

    workspace

    String

    工作目录路径,SDK从该路径读取配置文件。

    app_key

    String

    必须填“default”。

    token

    String

    必须填“default”。

    url

    String

    创建听悟实时记录任务时返回的会议MeetingJoinUrl作为音频流推送地址,在后续实时音频流识别时通过该地址进行推流。

    service_mode

    String

    必须填“1”,表示启用在线功能。

    device_id

    String

    设备标识,唯一表示一台设备(如Mac地址/SN/UniquePsuedoID等)。

    debug_path

    String

    debug目录。当初始化SDK时的save_log参数取值为true时,该目录用于保存日志文件。

    save_wav

    String

    当初始化SDK时的save_log参数取值为true时,该参数生效。表示是否保存音频debug,该数据保存在debug目录中,需要确保debug_path有效可写。

    注意,音频文件无上限,请注意持续存储导致磁盘存满。

    其中,INativeNuiCallback类型包含如下回调。

    • onNuiAudioStateChanged:根据音频状态进行录音功能的开关。

      /**
       * 当start/stop/cancel等接口调用时,SDK通过此回调通知App进行录音的开关操作。
       * @param state:录音需要的状态(打开/关闭)
       */
      void onNuiAudioStateChanged(AudioState state);
    • onNuiNeedAudioData:在回调中提供音频数据。

      /**
       * 开始识别时,此回调被连续调用,App需要在回调中进行语音数据填充。
       * @param buffer:填充语音的存储区。
       * @param len:需要填充语音的字节数。
       * @return:实际填充的字节数。
       */
      int onNuiNeedAudioData(byte[] buffer, int len);
    • onNuiEventCallback:SDK事件回调。

      /**
       * SDK主要事件回调
       * @param event:回调事件,参见如下事件列表。
       * @param resultCode:参见错误码,在出现EVENT_ASR_ERROR事件时有效。
       * @param arg2:保留参数。
       * @param kwsResult:语音唤醒功能(暂不支持)。
       * @param asrResult:语音识别结果。
       */
      void onNuiEventCallback(NuiEvent event, final int resultCode, final int arg2, KwsResult kwsResult, AsrResult asrResult);

      事件列表:

      名称

      说明

      EVENT_VAD_START

      检测到人声起点。

      EVENT_VAD_END

      检测到人声尾点。

      EVENT_ASR_PARTIAL_RESULT

      语音识别中间结果。

      EVENT_ASR_ERROR

      根据错误码信息判断出错原因。

      EVENT_MIC_ERROR

      录音错误,表示SDK连续2秒未收到任何音频,可检查录音系统是否正常。

      EVENT_SENTENCE_START

      实时语音识别事件,表示检测到一句话开始。

      EVENT_SENTENCE_END

      实时语音识别事件,表示检测到一句话结束,返回一句完整的结果。

      EVENT_SENTENCE_SEMANTICS

      暂不使用。

      EVENT_RESULT_TRANSLATED

      翻译结果。

      EVENT_TRANSCRIBER_COMPLETE

      停止语音识别后最终事件

    • onNuiAudioRMSChanged:音频能量值回调。

      /**
       * 运行过程中收到音频的实时音频能量值
       * @param val: 音频数据能量值回调,范围-160至0,一般用于UI展示语音动效
       */
      public void onNuiAudioRMSChanged(float val);

  • setParams:以JSON格式设置SDK参数。

    /**
     * 以JSON格式设置参数。接口需要在initialize之后startDialog之前调用。
     * @param params:参数信息请参见如下说明。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public synchronized int setParams(String params);

    params详细说明:

    参数

    类型

    是否必选

    说明

    service_type

    Integer

    必须填“4”。此为需要请求的语音服务类型,听悟实时推流为“4”。

    nls_config

    JsonObject

    访问语音服务相关的参数配置,详见如下。

    nls_config.sr_format

    String

    必须填“pcm”。对应的《CreateTask - 创建听悟任务》中,创建听悟任务时也请指定音频流数据的编码格式为pcm。

    nls_config.sample_rate

    Integer

    音频采样率,默认值:16000Hz。对应的《CreateTask - 创建听悟任务》中,创建听悟任务时也请指定音频流数据的采样率,当前支持 8000 和 16000。

  • startDialog:开始识别。

    /**
     * 开始识别
     * @param vad_mode:多种模式,对于识别场景,请使用P2T。
     * @param dialog_params:json string形式的对话参数,可不设置直接传入空JSON字符串。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public synchronized int startDialog(VadMode vad_mode, String dialog_params);
  • stopDialog:结束识别。

    /**
     * 结束识别,调用该接口后,服务端将返回最终识别结果并结束任务。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public synchronized int stopDialog();
  • cancelDialog:立即结束识别。

    /**
     * 立即结束识别,调用该接口后,不等待服务端返回最终识别结果就立即结束任务。
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public synchronized int cancelDialog();
  • release:释放SDK。

    /**
     * 释放SDK资源
     * @return:参见错误码:https://help.aliyun.com/document_detail/459864.html。
     */
    public synchronized int release();
  • GetVersion:获得当前SDK版本信息。

    /**
     * 获得当前SDK版本信息
     * @return: 字符串形式的SDK版本信息
     */
    public synchronized String GetVersion();

调用步骤

重要

请下载后在听悟的样例初始化代码中将Appkey和Token置为default,url置为您创建听悟实时记录返回的会议MeetingJoinUrl。

  1. 初始化SDK、录音实例。

  2. 根据业务需求设置参数。

  3. 调用startDialog开始识别。

  4. 根据音频状态回调onNuiAudioStateChanged,打开录音机。

  5. 在onNuiNeedAudioData回调中提供录音数据。

  6. 在EVENT_SENTENCE_START事件回调中表示当前开始识别一个句子,在EVENT_ASR_PARTIAL_RESULT事件回调中获取识别中间结果,在EVENT_SENTENCE_END事件回调中获得这句话完整的识别结果和各相关信息,在EVENT_RESULT_TRANSLATED事件回调中获得翻译结果。

  7. 调用stopDialog结束识别。并从EVENT_TRANSCRIBER_COMPLETE事件回调确认已停止识别。

  8. 结束调用,使用release接口释放SDK资源。

Proguard配置

如果代码使用了混淆,请在proguard-rules.pro中配置:

-keep class com.alibaba.idst.nui.*{*;}

代码示例

说明

您如果有多例需求,也可以直接new对象进行使用。也可采用GetInstance获得单例。

NUI SDK初始化

//这里获得资源路径, 即工作路径
//  内部通过context.getApplicationContext().getFilesDir().toString() + "/asr_my" 创建工作路径,
//  例如 /data/user/0/mit.alibaba.nuidemo/files/asr_my
String workspace = CommonUtils.getModelPath(this);

//创建debug路径
String debug_path = getExternalCacheDir().getAbsolutePath() + "/debug_" + System.currentTimeMillis();
Utils.createDir(debug_path);

//从nuisdk.aar中assets资源拷贝到workspace中
CommonUtils.copyAssetsData(this);

//初始化SDK,注意用户需要在genInitParams中填入相关ID信息才可以使用。
NativeNui nui_instance = new NativeNui();
int ret = nui_instance.initialize(this, genInitParams(asset_path,debug_path), Constants.LogLevel.LOG_LEVEL_VERBOSE, true);

其中,genInitParams生成为String JSON字符串,包含资源目录和用户信息。其中用户信息包含如下字段。

private String genInitParams(String workpath, String debugpath) {
    String str = "";
    try{
        // 1. 接口与实现:https://help.aliyun.com/zh/tingwu/interface-and-implementation
        //    按文档步骤,首先创建AccessKey和创建项目
        //    然后需要用户在自己的服务端调用CreateTask接口创建实时记录,获得MeetingJoinUrl
        //    此MeetingJoinUrl即为下方url
        JSONObject object = new JSONObject();

        //账号和项目创建
        //  ak_id ak_secret 如何获得,请查看https://help.aliyun.com/document_detail/72138.html
        object.put("app_key", "default"); // 必填
        object.put("token", "default"); // 必填

        // url中填入生成的MeetingJoinUrl。
        // 由于MeetingJoinUrl生成过程涉及ak/sk,移动端不可存储账号信息,故需要在服务端生成,并下发给移动端。
        // 详细请看: https://help.aliyun.com/zh/tingwu/api-tingwu-2023-09-30-createtask
        object.put("url", "wss://tingwu-realtime-cn-hangzhou-pre.aliyuncs.com/api/ws/v1?xxxx"); // 必填

        //工作目录路径,SDK从该路径读取配置文件
        object.put("workspace", workpath); // 必填, 且需要有读写权限
        object.put("device_id", Utils.getDeviceId()); // 必填, 推荐填入具有唯一性的id, 方便定位问题
        //当初始化SDK时的save_log参数取值为true时,该参数生效。表示是否保存音频debug,该数据保存在debug目录中,需要确保debug_path有效可写。
//        object.put("save_wav", "true");
        //debug目录,当初始化SDK时的save_log参数取值为true时,该目录用于保存中间音频文件。
        object.put("debug_path", debugpath);

        object.put("service_mode", Constants.ModeFullCloud); // 必填

        str = object.toString();
    } catch (JSONException e) {
        e.printStackTrace();
    }

    // 注意! str中包含ak_id ak_secret token app_key等敏感信息, 实际产品中请勿在Log中输出这类信息!
    Log.i(TAG, "InsideUserContext:" + str);
    return str;
}

参数设置

以JSON字符串形式进行设置。

//设置相关识别参数,具体参考API文档
//  initialize()之后startDialog之前调用
nui_instance.setParams(genParams());

private String genParams() {
    String params = "";
    try {
        JSONObject nls_config = new JSONObject();
        nls_config.put("sample_rate", 16000);
        nls_config.put("sr_format", "pcm");
        JSONObject tmp = new JSONObject();
        tmp.put("nls_config", nls_config);
        tmp.put("service_type", Constants.kServiceTypeSpeechTranscriber); // 必填
        params = tmp.toString();
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return params;
}

开始识别

通过startDialog接口开启监听。

nui_instance.startDialog(Constants.VadMode.TYPE_P2T, genDialogParams());

private String genDialogParams() {
    String params = "";
    try {
        JSONObject dialog_param = new JSONObject();
        params = dialog_param.toString();
    } catch (JSONException e) {
        e.printStackTrace();
    }
    return params;
}

回调处理

  • onNuiAudioStateChanged:录音状态回调,SDK内部维护录音状态,根据该状态的回调进行录音机的开关操作。

    public void onNuiAudioStateChanged(Constants.AudioState state) {
        Log.i(TAG, "onNuiAudioStateChanged");
        if (state == Constants.AudioState.STATE_OPEN) {
            Log.i(TAG, "audio recorder start");
            mAudioRecorder.startRecording();
        } else if (state == Constants.AudioState.STATE_CLOSE) {
            Log.i(TAG, "audio recorder close");
            mAudioRecorder.release();
        } else if (state == Constants.AudioState.STATE_PAUSE) {
            Log.i(TAG, "audio recorder pause");
            mAudioRecorder.stop();
        }
    }
  • onNuiNeedAudioData:录音数据回调,在该回调中填充录音数据。

    public int onNuiNeedAudioData(byte[] buffer, int len) {
        int ret = 0;
        if (mAudioRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
            Log.e(TAG, "audio recorder not init");
            return -1;
        }
        ret = mAudioRecorder.read(buffer, 0, len);
        //返回值告知SDK读到了多少数据。
        //如果返回<0,则表示出错。
        //返回0,则表示无录音数据,连续2s返回0,会触发事件EVENT_MIC_ERROR。
        return ret;
    }
  • onNuiEventCallback:NUI SDK事件回调,请勿在事件回调中调用SDK的接口,可能引起死锁。

    public void onNuiEventCallback(Constants.NuiEvent event, final int resultCode, final int arg2, KwsResult kwsResult, AsrResult asrResult) {
        Log.i(TAG, "event=" + event + " resultCode=" + resultCode);
        // asrResult包含task_id,task_id有助于排查问题,请用户进行记录保存。
        //
        // 新版本新增asrResult.allResponse,若为非nullptr和非空,则给出json格式字符串的完整信息。
        if (event == Constants.NuiEvent.EVENT_TRANSCRIBER_COMPLETE) {
          // 实时识别结束
        } else if (event == Constants.NuiEvent.EVENT_ASR_PARTIAL_RESULT) {
          // 例如展示当前句子的识别中间结果
          showText(asrView, asrResult.asrResult);
        } else if (event == Constants.NuiEvent.EVENT_SENTENCE_END) {
          // 例如展示当前句子的完整识别结果
          showText(asrView, asrResult.asrResult);
        } else if (event == Constants.NuiEvent.EVENT_RESULT_TRANSLATED) {
          // 例如展示当前句子的翻译结果
          showText(asrView, asrResult.asrResult);
        } else if (event == Constants.NuiEvent.EVENT_ASR_ERROR) {
          // asrResult在EVENT_ASR_ERROR中为错误信息,搭配错误码resultCode和其中的task_id更易排查问题,请用户进行记录保存。
        } else if (event == Constants.NuiEvent.EVENT_MIC_ERROR) {
          // EVENT_MIC_ERROR表示2s未传入音频数据,请检查录音相关代码、权限或录音模块是否被其他应用占用。
        } else if (event == Constants.NuiEvent.EVENT_DIALOG_EX) { /* unused */
          // 此事件可不用关注
      }
    }

结束识别

nui_instance.stopDialog();

释放SDK

nui_instance.release();