【平头哥RVB2601开发板试用体验】基于 HTTPClient 的云语音识别 2

简介: HTTPClient 组件源码解析

作者 | 哈猪猪


前言

在文章1中我们计划用 HTTPClient 组件帮助我们与服务器通信,上传录音文件并接收识别结果,最终实现云语音识别。经过测试后没有问题,因此接下来我们需要理解该组件提供的功能,并实现文件对服务器的上传。


源码解析

首先,根据 HTTPClient 组件的示例程序 http_examples.c,我们得知了一个完整的 HTTP 请求需要调用的接口流程,大致如下。以 POST 请求为例,并直接设置 url 而非 host&path:

http_client_config_t config = {
    .url = "http://httpbin.org/get",
    .event_handler = _http_event_handler,
};
http_client_handle_t client = http_client_init(&config);
const char *post_data = "field1=value1&field2=value2";
http_client_set_method(client, HTTP_METHOD_POST);
http_client_set_post_field(client, post_data, strlen(post_data));
err = http_client_perform(client);
if (err == HTTP_CLI_OK) {
    LOGI(TAG, "HTTP POST Status = %d, content_length = %d \r\n",
            http_client_get_status_code(client),
            http_client_get_content_length(client));
} else {
    LOGE(TAG, "HTTP POST request failed: 0x%x @#@@@@@@", (err));
    e_count ++;
}


整个流程主要涉及到以下几个函数:

http_client_init() 主要是基于 config 对 client 结构的各种变量赋初值,包括在请求中会用的到的各种缓存空间,以及 header 中的基本信息。其中 config 未设置的会赋缺省值。也可以用库提供的各种 set_xxx 函数来对变量赋值。

http_client_set_post_field():对于有请求体的 POST,我们需要调用这个函数将发送数据的指针以及长度赋予 client:

web_err_t http_client_set_post_field(http_client_handle_t client, const char *data, int len)
{
    web_err_t err = WEB_OK;
    client->post_data = (char *)data;   // 待发送数据指针
    client->post_len = len;             // 待发送数据长度
    LOGD(TAG, "set post file length = %d", len);
    // 如果此前没有设置 Content-Type,则设置为 application/x-www-form-urlencoded
    if (client->post_data)
    {
        char *value = NULL;
        if ((err = http_client_get_header(client, "Content-Type", &value)) != WEB_OK)
        {
            return err;
        }
        if (value == NULL)
        {
            err = http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
        }
    }
    else
    {
        client->post_len = 0;
        err = http_client_set_header(client, "Content-Type", NULL);
    }
    return err;
}


阅读源码可见,库默认支持“Content-Type”为“application/x-www-form-urlencoded”的 POST 请求,而我们要上传音频文件是“multipart/form-data”类型的。然而浏览了整个 HTTPClient 组件发现它就支持这一种类型……POST 的具体实现也只支持这一种。我们接着往下看就知道了……

http_client_perform():无论是什么 HTTP 请求,在设置完 client 之后最后都是经由这一个函数来具体实现。该函数将根据当前所处的 HTTP 请求流程,调用对应的函数,而这些函数最终会调用 transport 组件即在传输层上读写,最终实现整个 HTTP 请求。http_client_perform() 依次主要调用了以下几个函数:

http_client_prepare();
http_client_connect();
http_client_request_send();
http_client_send_post_data();
http_client_fetch_headers();
http_check_response();
http_client_get_data();
http_client_close();


实现的功能与其函数名一致,也是我们熟知的 HTTP 请求流程。其中我们最关心的当然是http_client_send_post_data():

static web_err_t http_client_send_post_data(http_client_handle_t client)
{   
    // 此时请求头应发送完毕
    if (client->state != HTTP_STATE_REQ_COMPLETE_HEADER)
    {
        LOGE(TAG, "Invalid state");
        return WEB_ERR_INVALID_STATE;
    }
    // 没有要发送的 post_data 直接返回(包括其它请求)
    if (!(client->post_data && client->post_len))
    {
        goto success;
    }
    // 发送 post_data
    int wret = http_client_write(client, client->post_data + client->data_written_index, client->data_write_left);
    if (wret < 0)
    {
        return wret;
    }
    // 在 request send 里面 data_write_left 最后被赋值为 post_len
    client->data_write_left -= wret;
    // 在 request send 里面 data_written_index 最后被赋值为 0
    client->data_written_index += wret;
    // 发送完毕
    if (client->data_write_left <= 0)
    {
        goto success;
    }
    else
    {
        return ERR_HTTP_WRITE_DATA;
    }
success:
    // 更新状态
    client->state = HTTP_STATE_REQ_COMPLETE_DATA;
    return WEB_OK;
}


可见,整个 POST 实现只会发送一次 post_data,它指向我们最初在 http_client_set_post_field() 中填入的待发送数据。然而,我们知道要发送“multipart/form-data”类型的数据,需要按照格式用 boundary 对请求体进行封装,并在请求头中提前告知 boundary。当然,HTTPClient 组件是没有实现这些的……因此如果我们要利用 HTTPClient 发送音频数据,就必须手动实现“multipart/form-data”类型请求体的封装与发送。

当我终于大致理解了 HTTPClient 组件后,我在浏览 YOC 源码时发现了一个就叫“http”的组件,它并没有出现在 YOC 文档里然而它已经实现了“multipart/form-data”类型的 POST……囿于我技术水平有限,源码缺少注释,感觉学习成本过高,遂放弃。

事实上针对本项目要实现的云语音识别,YOC 还有很多更适合的组件:麦克风服务,云音交互(AUI Cloud)……甚至后者本身就是为语音识别等应用构建的。然而由于源码没有注释,只有接口介绍文档,以及其它板子上的相关例程,感觉“移植”组件对自己难度过大,最终都没有使用,选择了基于 HTTPClient 手动实现音频数据的发送。


本文源自:平头哥芯片开放社区

欢迎关注公众号:芯片开放社区(ID:OCC_THEAD),查看更多应用实战文章。

相关实践学习
达摩院智能语音交互 - 声纹识别技术
声纹识别是基于每个发音人的发音器官构造不同,识别当前发音人的身份。按照任务具体分为两种: 声纹辨认:从说话人集合中判别出测试语音所属的说话人,为多选一的问题 声纹确认:判断测试语音是否由目标说话人所说,是二选一的问题(是或者不是) 按照应用具体分为两种: 文本相关:要求使用者重复指定的话语,通常包含与训练信息相同的文本(精度较高,适合当前应用模式) 文本无关:对使用者发音内容和语言没有要求,受信道环境影响比较大,精度不高 本课程主要介绍声纹识别的原型技术、系统架构及应用案例等。 讲师介绍: 郑斯奇,达摩院算法专家,毕业于美国哈佛大学,研究方向包括声纹识别、性别、年龄、语种识别等。致力于推动端侧声纹与个性化技术的研究和大规模应用。
相关文章
|
网络协议 数据安全/隐私保护 芯片
|
存储 API 开发工具
【平头哥RVB2601创意应用开发】使用体验02 -- KV存储
使用KV组件在RVB2601进行项目参数的本地持久化。
578 0
【平头哥RVB2601创意应用开发】使用体验02 -- KV存储
|
网络协议 物联网 数据处理
【平头哥RVB2601开发板试用体验】AT 解析器和通过w800 AT命令接入阿里云生活物联网平台
RISC-V RVB2601 Yoc 实现和测试验证阿里云平台接入的等相关功能,同时去了解YoC中网络设备和AT解析器框架。
681 0
【平头哥RVB2601开发板试用体验】AT 解析器和通过w800 AT命令接入阿里云生活物联网平台
|
存储 JSON 数据格式
【平头哥RVB2601创意应用开发】疫情播报系统应用
实现通过网络读取json数据,进行分解,针对数据字符,播放相应的mp3音频文件,并在OLED屏幕上显示实际对应数据的疫情播报系统。
213 0
【平头哥RVB2601创意应用开发】疫情播报系统应用
|
机器学习/深度学习 人工智能 算法
|
存储 边缘计算 固态存储
玄铁RISC-V处理器入门与实战-平头哥玄铁CPU IP-面向高性能领域CPU
玄铁RISC-V处理器入门与实战-平头哥玄铁CPU IP-
1458 0
|
供应链 芯片
平头哥芯片采用的RISC-V架构
平头哥芯片采用的RISC-V架构
343 1
|
物联网 开发工具 开发者
玄铁RISC-V处理器入门与实战-平头哥RISC-V 工具链-剑池CDK开发工具
玄铁RISC-V处理器入门与实战-平头哥RISC-V 工具链
370 1