前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >手把手帮你视频转文本(2-音频转录)

手把手帮你视频转文本(2-音频转录)

原创
作者头像
技术路漫漫
修改2020-06-11 10:14:34
1.1K0
修改2020-06-11 10:14:34
举报
文章被收录于专栏:技术路漫漫技术路漫漫

这是本系列的第二篇,第一篇我们完成了将MP4视频转换为PCM音频,这篇我们实现基于百度云的录音转写,本文所有源代码参见:https://gitee.com/coolpine/thomas

对象存储服务调用

第一篇中,我们转换后的PCM文件,还是存储在本地文件系统中。接下来,我们需要基于百度云的对象存储BOS服务,将文件上传到云端:

  • 首先,我们需要开通BOS服务,获取相关access-key,建立相关的bucket。
  • 其次,参考官方API,引入相关maven依赖。
  • 最后,完成本地文件上传到云端bucket,同时将相关日志记录到本地MySQL数据库。

开通服务

具体服务开通过程忽略,补充说明下,选择百度云是因为语音转录是免费的,BOS虽然收费,但非常便宜,从本项目情况看,总共320MB左右的文件,一共花费不到1元钱,简直白菜价了。

先是获取到相关key后,在properties中配置进去:

代码语言:javascript
复制
#百度云BOS
thomas.bos.access-key-id=xxx
thomas.bos.secret-access-key=xx
thomas.bucket-name=xxx

依赖引入

具体引入的依赖是:

代码语言:javascript
复制
<groupId>com.baidubce</groupId>
<artifactId>bce-java-sdk</artifactId>
<version>0.10.105</version>

特别提示下,该依赖会连带引入很多第三方依赖,在通过maven-helper插件分析依赖时,发现很多依赖冲突的,例如log4j、commons-logging、slf4j-log4j12等,建议一并排除掉。

同时,因为本工程并未直接依赖com.google.guava,但在bce-java-sdk中,也存在该依赖冲突。参考的解决办法是:先在bce-java-sdk中排除com.google.guava依赖,同时单独再引入com.google.guava:

代码语言:javascript
复制
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>17.0</version>
</dependency>

文件上传程序编写

本项目中,我已将相关功能封装到了BosFileService中,主要是基于BosClient进行文件操作:

1、获取bucket下所有文件:

代码语言:javascript
复制
bosClient.listObjects(THOMAS_BUCKET_NAME).getContents();

2、基于文件key获取7天有效期的URL:

代码语言:javascript
复制
bosClient.generatePresignedUrl(THOMAS_BUCKET_NAME, objectKey, 7 * 24 * 60 * 60);

3、上传单个文件:

代码语言:javascript
复制
PutObjectResponse response = bosClient.putObject(THOMAS_BUCKET_NAME, key, filePath.toFile());

4、上传成功时,会返回eTag,将之记录到本地数据库:

代码语言:javascript
复制
fileUploadRepo.save(SpeechFileUploadInfo.builder().eTag(eTag).fileName(fileName).build());

5、批量上传目录下所有文件:

代码语言:javascript
复制
Files.list(rootPath).forEach(path -> {
    if (Files.isDirectory(path)) {
        //递归遍历目录
        count.addAndGet(batchUploadFile(path));
    } else {
        //上传该文件
        count.getAndAdd(uploadFile(path));
    }
});

录音转写服务调用

完成文件上传到云端BOS后,接下来基于百度云AI的语音识别(录音转写)服务,提交离线转写任务:

  • 开通免费的语音转录服务,获取相关key。
  • 基于restful api,提交转写任务。
  • 查询转写任务结果,将转写成功的结果,保存到本地数据库。

首先,将ai应用相关key记录在properties文件中,同时也一并记录相关api的调用路径:

代码语言:javascript
复制
thomas.ai.api-key=xxx
thomas.ai.secret-key=xxx
thomas.ai.access-url=https://aip.baidubce.com/oauth/2.0/token
thomas.ai.create-url=https://aip.baidubce.com/rpc/2.0/aasr/v1/create
thomas.ai.query-url=https://aip.baidubce.com/rpc/2.0/aasr/v1/query

本项目将语音转录功能封装在 SpeechService服务中。

在调用任何功能之前,需要先基于上述apikey等,获取access token,同时也可以将token缓存起来:

代码语言:javascript
复制
@Cacheable(value = "thomas-ai-token")
public Optional<String> getAccessToken() {
    Map<String, String> params = new HashMap<>(2);
    params.put("client_id", API_KEY);
    params.put("client_secret", SECRET_KEY);
    //token请求URL
    String requestUrl = ACCESS_TOKEN_URL
            + "?grant_type=client_credentials"
            + "&client_id={client_id}"
            + "&client_secret={client_secret}";
    String jsonStr = restTemplate.getForObject(requestUrl, String.class, params);
​
    JSONObject jsonObject = JSON.parseObject(jsonStr);
    return Optional.ofNullable(jsonObject.getString("access_token"));
}

为方便后续调用,封装了一个通用的doPost方法:

代码语言:javascript
复制
public Optional<ResponseEntity<String>> doPost(String url, boolean needToken, Map<String, Object> values) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
​
    //将请求参数转换为json
    String requestJson = JSON.toJSONString(values);
    HttpEntity<String> request = new HttpEntity<>(requestJson, headers);
​
    StringBuilder postUrl = new StringBuilder(url);
    //需要追加token
    if (needToken) {
        Optional<String> opToken = getAccessToken();
        if (opToken.isPresent()) {
            //token存在则追加token
            postUrl.append("?access_token=" + opToken.get());
        } else {
            log.error("没有获取到ACCESS TOKEN", opToken);
            return Optional.empty();
        }
    }
​
    return Optional.ofNullable(restTemplate.postForEntity(postUrl.toString(), request, String.class));
}

基于录音文件URL,创建文本转写任务:

代码语言:javascript
复制
//调用模式参见 https://ai.baidu.com/ai-doc/SPEECH/ck5diijkt
Map<String, Object> values = new HashMap<>(4);
values.put("speech_url", speechUrl);
values.put("format", "pcm");
values.put("pid", 1537);
values.put("rate", 16000);
​
return this.doPost(CREATE_URL, true, values);

提交任务后,API返回的是taskId,该id必须保存,因为后续需要基于该id查询转写结果:

代码语言:javascript
复制
//解析返回结果中的taskid,能解析到即代表提交成功
String taskId = JSON.parseObject(responseEntity.get().getBody()).getString("task_id");

将解析得到的id,保存到数据库中(本项目是基于JPA来进行数据库操作):

代码语言:javascript
复制
SpeechTaskInfo taskInfo = SpeechTaskInfo.builder()
        .taskId(taskId)
        .taskStatus(SpeechTaskStatus.Running)
        .pcmKey(f.getKey())
        .pcmUrl(url)
        .build();
taskInfoRepo.save(taskInfo);

转录结果查询及存储

录音转写任务提交成功,最后一步就是等待离线任务运行完成,任务状态划分如下:

代码语言:javascript
复制
/** 转写中 */
Running,
​
/** 转写成功 */
Success,
​
/** 转写失败 */
Failure

在SpeechService中,封装了updateTaskResults方法,实现对任务的查询,并将转写成功的记录,记录到数据库中:

  • 首先,遍历数据库中所有 Running状态的任务
  • 其次,将所有任务taskId拼接后,调用任务运行结果批量查询API。
  • 最后,判断API结果,并记录转写任务明细到数据库。

批量查询转录结果的调用非常简单:

代码语言:javascript
复制
// 技术文档 https://ai.baidu.com/ai-doc/SPEECH/6k5dilahb
Map<String, Object> values = new HashMap<>(1);
values.put("task_ids", taskIds);
​
return this.doPost(QUERY_URL, true, values);

处理API返回结果时,我们是采用的阿里巴巴的fastjson,实现将api返还的json对象,转换为java对象:

代码语言:javascript
复制
SpeechLogInfo logInfo = JSON.parseObject(responseEntity.get().getBody(), SpeechLogInfo.class);
// 分析每个解析任务的运行状态
logInfo.getTasks_info()
        .stream()
        .filter(infoBean -> infoBean.getTask_status().equals(SpeechTaskStatus.Success.name()))
        .forEach(infoBean -> {
            // 处理每个解析成功的任务
            infoBean.getTask_result().getDetailed_result().forEach(r -> {
                // 遍历每个解析结果,并存储到数据库中
                SpeechTaskResult result = SpeechTaskResult.builder()
                        .taskId(infoBean.getTask_id())
                        .beginTime(r.getBegin_time() / 1000)
                        .endTime(r.getEnd_time() / 1000)
                        .words(String.join("", r.getRes()))
                        .build();
                taskResultRepo.save(result);
            });
            // 更新任务为成功状态
            Optional<SpeechTaskInfo> taskInfo = taskInfoRepo.findById(infoBean.getTask_id());
            if (taskInfo.isPresent()) {
                SpeechTaskInfo info = taskInfo.get();
                //设置为成功状态
                info.setTaskStatus(SpeechTaskStatus.Success);
                //保存到数据库
                taskInfoRepo.save(info);
                count.incrementAndGet();
            }
​
        });

补充说明下,推荐在idea中安装GsonFormat插件,实现基于json格式字符串,快速创建java对象SpeechLogInfo。

到此,我们将完成了将PCM文件上传到云端,并实现调用录音转写服务,解析得到文本内容,如果相关问题或疑问,欢迎给我留言。最后一篇,我们将实现读取数据库的转录结果,导出为一个完整的word文档,方便阅读和分享。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 对象存储服务调用
    • 开通服务
      • 依赖引入
        • 文件上传程序编写
        • 录音转写服务调用
        • 转录结果查询及存储
        相关产品与服务
        数据库
        云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档