使用七牛云对音频文件转码,并对比ffmpeg

需求来源:

       目前,公司项目功能需求,需要在微信上录音,并将录音文件下载后上传至七牛云,以供后期使用。微信上下载的录音文件是amr格式的,而公司项目需要的是m4a格式,这就引出了一个问题:如何对音频文件进行转格式并且上传到七牛云上?

存在问题:

       上网查阅一些资料后,发现大部分人都使用的是ffmpeg进行格式转换,但考虑之后觉得还是存在一些问题的。 1、ffmpeg操作的对象需要是本地存在的文件,这就需要先将录音文件下载到服务器本地,然后进行格式转换操作后再把新文件上传至七牛云上,这将会进行一定量的IO和网络操作,增加服务器的负担。2、执行转码命令后需要使用proc.waitFor()一直等待结果,但是容易造成阻塞,如果不使用则无法获知转码何时完成,以及转码是否成功。

/**
 * 将amr文件输入转为mp3格式
 * @param file
 * @return
 */
public static InputStream amrToMP3(MultipartFile file) {
    String ffmpegPath = getLinuxOrWindowsFfmpegPath();
    Runtime runtime = Runtime.getRuntime();
    try {
        String filePath = copyFile(file.getInputStream(), file.getOriginalFilename());

        String substring = filePath.substring(0, filePath.lastIndexOf("."));

        String mp3FilePath = substring + ".mp3";

        //执行ffmpeg文件,将amr格式转为mp3
        //filePath ----> amr文件在临时文件夹中的地址
        //mp3FilePath  ----> 转换后的mp3文件地址
        Process p = runtime.exec(ffmpegPath + "ffmpeg -i " + filePath + " " + mp3FilePath);//执行ffmpeg.exe,前面是ffmpeg.exe的地址,中间是需要转换的文件地址,后面是转换后的文件地址。-i是转换方式,意思是可编码解码,mp3编码方式采用的是libmp3lame

        //释放进程
        p.getOutputStream().close();
        p.getInputStream().close();
        p.getErrorStream().close();
        p.waitFor();

        File mp3File = new File(mp3FilePath);
        InputStream fileInputStream = new FileInputStream(mp3File);

        //应该在调用该方法的地方关闭该input流(使用完后),并且要删除掉临时文件夹下的相应文件
        /*File amrFile = new File(filePath);
        File mp3File = new File(mp3FilePath);
        if (amrFile.exists()) {
            boolean delete = amrFile.delete();
            System.out.println("删除源文件:"+delete);
        }
        if (mp3File.exists()) {
            boolean delete = mp3File.delete();
            System.out.println("删除mp3文件:"+delete);
        }*/

        return fileInputStream;
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        runtime.freeMemory();
    }
    return null;
}

代码引自 https://www.cnblogs.com/Amaris-Lin/p/9186888.html

       上述代码,可以很直观地看到,当执行完命令后要用p.waitFor();一直等待结果,之后才能进行后续文件的读取。但是waitFor()方法容易导致阻塞,这会影响整个服务端的稳定性,一直长时间等待也会影响接口响应。

解决方案:

       结合上述两个问题,以及查阅一定的技术博客,最终选择尝试七牛云的转码。使用异步方法将从微信上下载下来的音频文件的流直接转存到七牛云,然后调用七牛云接口执行转码命令,当转码结束后无论转码是否成功,七牛云都会调用回调接口将转码结果返回。

// 将amr音频文件转码成acc格式
public Map arm2aac(String mediaKey) throws IOException {
    Map ret = new HashMap();

    String acceptKey = mediaKey + ".m4a";
    String urlbase64 = UrlSafeBase64.encodeToString(bucketname + ":" + acceptKey);
    String vframeJpgFop = String.format("avthumb/m4a/ab/64k/ar/22050/acodec/aac|saveas/%s", urlbase64);

    // 数据处理队列名称,#必须#
    String persistentPipeline = "test-pipeline";
    // 数据处理完成结果通知地址(自行配置)
    String persistentNotifyUrl = "http://test.abc.com/site/transcodeNotify";
    // 构造一个带指定Zone对象的配置类
    Configuration cfg = new Configuration(Zone.zone0());
    // 构建持久化数据处理对象
    OperationManager operationManager = new OperationManager(auth, cfg);
    try {
        // 执行持久化任务处理,获取任务ID,可用于查询任务处理进度
        String persistentId = operationManager.pfop(bucketname, mediaKey + ".amr", vframeJpgFop, persistentPipeline, persistentNotifyUrl, true);
        // 可以根据该 persistentId 查询任务处理进度
        // OperationStatus operationStatus = operationManager.prefop(persistentId);
        ret.put("persistentId", persistentId);
    } catch (QiniuException e) {
        ret.put("errmsg", e.response.error);
        Response r = e.response;
        // 请求失败时打印的异常的信息
        logger.error(r.toString());
    }
    return ret;
}

七牛云具体的音视频处理使用说明可以参考开发者中心的文档

https://developer.qiniu.com/dora/manual/1248/audio-and-video-transcoding-avthumb

总结:

       以上两种转码方法有各自适用场景,使用者需要结合自身需求来确定选择何种方式。本人最终选择七牛云转码的原因是,公司项目要求不能等待太长时间,而七牛云提供了非常棒的回调机制。
       由于本人知识储备量的局限性,本文还有很多不足的地方,或者理解错误之处,敬请谅解,并希望能及时指出。

你可能感兴趣的:(使用七牛云对音频文件转码,并对比ffmpeg)