超大文件调用讯飞语音听写解决方案

需求

2019年6月20日
1,案场销售和客服的谈话录音,需要将其翻译成文字,入库,后台审核。

需求分析

技术选型
讯飞接口
语音有方言
1,因为方言的存在,只能使用语音听写接口。
拿到的是语音文件
1,所以只能使用WebAPI方式调用讯飞接口。
讯飞语音听写有音频格式限制
1,所以只能使用ffmpeg进行格式转换
2,[下载地址](http://www.ffmpeg.org/download.html)
3,[基本使用](https://doc.xfyun.cn/rest_api/%E9%9F%B3%E9%A2%91%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E.html)
接口对语音时长限制60秒内
1,将转换格式后的文件根据时长进行分片。
2,采用多线程将分割后的文件交由讯飞处理,等待返回结果(语音听写没有异步接口)。
3,根据分片计算每个线程需要处理的分片数量,采用分页思路,决定开启多少线程。
4,将处理结果放入一个并发集合中(生产环境在redis中)
5,如果当期分片失败,或者异常,使用补偿机制,默认5次,如果还失败手动补偿。
6,将结果集进行拼接格式化。

实现思路

1,用户分片上传文件到服务器,服务器进行合并。
2,文件合并完成后生成一条数据,将id放入MQ中。
3,消费者接收到MQ中的消息,将服务器文件下载到当前机器,进行格式化,分片操作。
4,分片完成后进行任务分配。
5,获取执行结果,进行合并,并将其更新到数据库中。

总结

1,典型的生产-消费模式
2,使用MQ原因,因为并不是主线业务,不能让其占用了太多内存和带宽,所以采用单线程处理。
3,对大文件进行先分后合中间采用多线程提高效率。
4,采用补偿机制,提高结果的可靠性,最后提供手动补偿,也是防止程序补偿失败问题。

实现代码

1,文件转换工具类
package com.itpengwei.util;

import java.io.File;
import java.io.InputStreamReader;
import java.io.LineNumberReader;

/**
 * @author 彭伟
 * @Date 2019/6/21 15:50
 * 文件格式转换工具类
 */
public class Video2Voice {

    // private static Logger logger = Logger.getLogger(Video2Voice.class);

    /**
     * 将音频文件转16k的pcm文件
     *
     * @param fileName
     * @return 音频文件名
     * @throws Exception
     */
    public static String transform(String fileName) throws Exception {

        File file = new File(fileName);
        if (!file.exists()) {
            System.out.println("文件不存在:" + fileName);
            throw new RuntimeException("文件不存在:" + fileName);
        }

        // 讯飞现在支持pcm,wav的语音流文件
        String name = fileName.substring(0, fileName.lastIndexOf(".")) + ".pcm";
        System.out.println("获取到的音频文件:" + name);
        // 提取视频中的音频文件。 根据讯飞要求设置采样率, 位数,
        File ffmpegPath = new File("D:\\软件\\ffmpeg-20190620-86f04b9-win64-static\\bin\\ffmpeg.exe");
        String cmd = ffmpegPath + " -y -i " + fileName + " -acodec pcm_s16le -f s16le -ac 1 -ar 16000 " + name;
        System.out.println("命令===》" + cmd);
        File tagret = new File(name);
        if (tagret.exists()) {
            System.out.println("文件存在,删除文件,进行覆盖操作");
            tagret.delete();
        }
        Process process = Runtime.getRuntime().exec(cmd);// 执行命令
        InputStreamReader ir = new InputStreamReader(process.getInputStream());
        LineNumberReader input = new LineNumberReader(ir);
        String line;
        // 输出结果,需要有这部分代码, 否则不能生产抽取的音频文件
        while ((line = input.readLine()) != null) {
            System.out.println(line);
        }
        process.destroy();
        return name;
    }

    public static void main(String[] args) {
        try {
            String transform = transform("C:\\Users\\彭伟\\Desktop\\temp\\java.mp3");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
2,文件分片工具类
package com.itpengwei.xunfei;

import com.itpengwei.util.Video2Voice;
import it.sauronsoftware.jave.Encoder;
import it.sauronsoftware.jave.MultimediaInfo;

import java.io.*;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 彭伟
 * @Date 2019/6/21 15:50
 * 文件操作工具类
 */
public class FileUtil {
    /**
     * 读取文件内容为二进制数组
     *
     * @param filePath
     * @return
     * @throws IOException
     */
    public static byte[] read(String filePath) throws IOException {
        InputStream in = new FileInputStream(filePath);
        byte[] data = inputStream2ByteArray(in);
        in.close();

        return data;
    }

    /**
     * 流转二进制数组
     *
     * @param in
     * @return
     * @throws IOException
     */
    private static byte[] inputStream2ByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }

    /**
     * 保存文件
     *
     * @param filePath
     * @param fileName
     * @param content
     */
    public static void save(String filePath, String fileName, byte[] content) {
        try {
            File filedir = new File(filePath);
            if (!filedir.exists()) {
                filedir.mkdirs();
            }
            File file = new File(filedir, fileName);
            OutputStream os = new FileOutputStream(file);
            os.write(content, 0, content.length);
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值