1、首先需要下载FFmpeg;
2、Gradle依赖
def void forceVersion(details, group, version) { if (details.requested.group == group) { details.useVersion version } } def void forceVersion(details, group, name, version) { if (details.requested.group == group && details.requested.name == name) { details.useVersion version } } allprojects { p -> group = 'com.my.spider' version = '1.0.0' apply plugin: 'java' apply plugin: 'maven' apply plugin: 'maven-publish' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' jar.doFirst { manifest { def manifestFile = "${projectDir}/META-INF/MANIFEST.MF" if (new File(manifestFile).exists()) from (manifestFile) attributes 'Implementation-Title':p.name if (p.version.endsWith('-SNAPSHOT')) { attributes 'Implementation-Version': p.version + '-' + p.ext.Timestamp } else { attributes 'Implementation-Version': p.version } attributes 'Implementation-BuildDateTime':new Date() } } javadoc { options { encoding 'UTF-8' charSet 'UTF-8' author false version true links 'http://docs.oracle.com/javase/8/docs/api/index.html' memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PRIVATE } } if (System.env.uploadArchives) { build.dependsOn publish } buildscript { repositories { mavenCentral() } dependencies {classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.5.14.RELEASE' } } afterEvaluate {Project project -> if (project.pluginManager.hasPlugin('java')) { configurations.all { resolutionStrategy.eachDependency {DependencyResolveDetails details -> forceVersion details, 'org.springframework.boot', '1.4.1.RELEASE' forceVersion details, 'org.slf4j', '1.7.21' forceVersion details, 'org.springframework', '4.3.3.RELEASE' } exclude module:'slf4j-log4j12' exclude module:'log4j' } dependencies {testCompile 'junit:junit:4.12' } } } repositories { mavenCentral() } // 时间戳:年月日时分 p.ext.Timestamp = new Date().format('yyyyMMddHHmm') // Build Number p.ext.BuildNumber = System.env.BUILD_NUMBER if (p.ext.BuildNumber == null || "" == p.ext.BuildNumber) { p.ext.BuildNumber = 'x' } } task zipSources(type: Zip) { description '压缩源代码' project.ext.zipSourcesFile = project.name + '-' + project.version + '-' + project.ext.Timestamp + '.' + project.ext.BuildNumber + '-sources.zip' archiveName = project.ext.zipSourcesFile includeEmptyDirs = false from project.projectDir exclude '**/.*' exclude 'build/*' allprojects.each { p -> exclude '**/' + p.name + '/bin/*' exclude '**/' + p.name + '/build/*' exclude '**/' + p.name + '/data/*' exclude '**/' + p.name + '/work/*' exclude '**/' + p.name + '/logs/*' } } def CopySpec appCopySpec(Project prj, dstname = null) { if (!dstname) { dstname = prj.name } return copySpec{ // Fat jar from (prj.buildDir.toString() + '/libs/' + prj.name + '-' + project.version + '.jar') { into dstname } // Configs from (prj.projectDir.toString() + '/config/examples') { into dstname + '/config' } // Windows start script from (prj.projectDir.toString() + '/' + prj.name + '.bat') { into dstname } // Unix conf script from (prj.projectDir.toString() + '/' + prj.name + '.conf') { into dstname rename prj.name, prj.name + '-' + project.version } } } task zipSetup(type: Zip, dependsOn: subprojects.build) { description '制作安装包' project.ext.zipSetupFile = project.name + '-' + project.version + '-' + project.ext.Timestamp + '.' + project.ext.BuildNumber + '-setup.zip' archiveName = project.name + '-' + project.version + '-' + project.ext.Timestamp + '.' + project.ext.BuildNumber + '-setup.zip' with appCopySpec(project(':spider-demo')) } import java.security.MessageDigest def generateMD5(final file) { MessageDigest digest = MessageDigest.getInstance("MD5") file.withInputStream(){is-> byte[] buffer = new byte[8192] int read = 0 while( (read = is.read(buffer)) > 0) { digest.update(buffer, 0, read); } } byte[] md5sum = digest.digest() BigInteger bigInt = new BigInteger(1, md5sum) return bigInt.toString(16) } task md5(dependsOn: [zipSetup, zipSources]) << { String md5_setup = generateMD5(file("${projectDir}/build/distributions/" + project.ext.zipSetupFile)); String md5_sources = generateMD5(file("${projectDir}/build/distributions/" + project.ext.zipSourcesFile)); println project.ext.zipSetupFile + '=' + md5_setup println project.ext.zipSourcesFile + '=' + md5_sources def newFile = new File("${projectDir}/build/distributions/" + project.name + '-' + project.version + '-' + project.ext.Timestamp + '.' + project.ext.BuildNumber + '-md5.txt') PrintWriter printWriter = newFile.newPrintWriter() printWriter.println project.ext.zipSetupFile + '=' + md5_setup printWriter.println project.ext.zipSourcesFile + '=' + md5_sources printWriter.flush() printWriter.close() } build.dependsOn subprojects.build, zipSetup, zipSources, md5
工程组件gradle依赖: 语音识别使用 百度api;需引入 compile 'com.baidu.aip:java-sdk:3.2.1'
apply plugin: 'spring-boot'
apply plugin: 'application'
distributions {
main {
contents {
from ("${projectDir}/config/examples") {
into "config"
}
}
}
}
distTar.enabled = false
springBoot {
executable = true
mainClass = 'com.my.ai.Application'
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web:1.4.0.RELEASE'
compile 'dom4j:dom4j:1.6.1'
compile 'commons-httpclient:commons-httpclient:3.1'
compileOnly 'com.h2database:h2:1.4.191'
compile 'javax.cache:cache-api:1.0.0'
compile 'org.jboss.resteasy:resteasy-jaxrs:3.0.14.Final'
compile 'org.jboss.resteasy:resteasy-client:3.0.14.Final'
// Axis
compile 'axis:axis:1.4'
compile 'org.jsoup:jsoup:1.10.1'
compile 'com.alibaba:fastjson:1.2.21'
compile 'com.baidu.aip:java-sdk:3.2.1'
}
3、视频抽取音频服务“
package com.my.ai.service; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; //视频抽取音频 @Service public class ExtractAudioService { public static Logger logger = LoggerFactory.getLogger(ExtractAudioService.class); public static void main(String[] args) { new ExtractAudioService().getAudioFromVideo("E:\\QLDownload\\氧化还原反应中电子转移的方向和数目的表示方法\\氧化还原反应中电子转移的方向和数目的表示方法.mp4", "D:\\ffmpeg4.2\\bin\\ffmpeg.exe"); } public String getAudioFromVideo(String videoPath,String ffmpegPath) { File video = new File(videoPath); if(video.exists() && video.isFile()){ String format = "wav"; String outPath = videoPath.substring(0,videoPath.lastIndexOf(".")) + ".wav"; processCmd(videoPath, ffmpegPath, format, outPath); return outPath; } return null; } //D:\ffmpeg4.2\bin\ffmpeg.exe -i 氧化还原反应中电子转移的方向和数目的表示方法.mp4 -f wav -vn -y 3.wav public String processCmd(String inputPath,String ffmpegPath,String format,String outPath) { List<String> commend = new java.util.ArrayList<String>(); commend.add(ffmpegPath); commend.add("-i"); commend.add(inputPath); commend.add("-y"); commend.add("-vn"); commend.add("-f"); commend.add(format); commend.add(outPath); try { ProcessBuilder builder = new ProcessBuilder(); builder.command(commend); builder.redirectErrorStream(true); Process p = builder.start(); // 1. start BufferedReader buf = null; // 保存ffmpeg的输出结果流 String line = null; // read the standard output buf = new BufferedReader(new InputStreamReader(p.getInputStream())); StringBuffer sb = new StringBuffer(); while ((line = buf.readLine()) != null) { System.out.println(line); sb.append(line); continue; } p.waitFor();// 这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行 // 1. end return sb.toString(); } catch (Exception e) { // System.out.println(e); return null; } } }
4、音频切段:
package com.my.ai.service;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class CutService {
public static Logger logger = LoggerFactory.getLogger(CutService.class);
public List<String> cutFile(String media_path, String ffmpeg_path) {
List<String> audios = new ArrayList<>();
int mediaTime = getMediaTime(media_path, ffmpeg_path);
int num = mediaTime / 59;
int lastNum = mediaTime % 59;
System.out.println(mediaTime +"|" + num + "|"+ lastNum);
int length = 59;
File file = new File(media_path);
String filename = file.getName();
for (int i = 0; i < num; i++) {
String outputPath = file.getParent() + File.separator + i + "-"+filename;
processCmd(media_path, ffmpeg_path, String.valueOf(length * i) ,
String.valueOf(length), outputPath);
audios.add(outputPath);
}
if(lastNum > 0) {
String outputPath = file.getParent() + File.separator + num + "-"+filename;
processCmd(media_path, ffmpeg_path, String.valueOf(length * num) ,
String.valueOf(lastNum), outputPath);
audios.add(outputPath);
}
return audios;
}
/**
* 获取视频总时间
*
* @param viedo_path 视频路径
* @param ffmpeg_path ffmpeg路径
* @return
*/
public int getMediaTime(String video_path, String ffmpeg_path) {
List<String> commands = new java.util.ArrayList<String>();
commands.add(ffmpeg_path);
commands.add("-i");
commands.add(video_path);
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commands);
final Process p = builder.start();
// 从输入流中读取视频信息
BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
StringBuffer sb = new StringBuffer();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line);
}
System.out.println(sb.toString());
br.close();
// 从视频信息中解析时长
String regexDuration = "Duration: (.*?), bitrate: (\\d*) kb\\/s";
Pattern pattern = Pattern.compile(regexDuration);
Matcher m = pattern.matcher(sb.toString());
if (m.find()) {
int time = getTimelen(m.group(1));
System.out
.println(video_path + ",视频时长:" + time + ",比特率:" + m.group(2) + "kb/s");
return time;
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
// 格式:"00:00:10.68"
public int getTimelen(String timelen) {
int min = 0;
String strs[] = timelen.split(":");
if (strs[0].compareTo("0") > 0) {
min += Integer.valueOf(strs[0]) * 60 * 60;// 秒
}
if (strs[1].compareTo("0") > 0) {
min += Integer.valueOf(strs[1]) * 60;
}
if (strs[2].compareTo("0") > 0) {
min += Math.round(Float.valueOf(strs[2]));
}
return min;
}
//D:\ffmpeg4.2\bin\ffmpeg.exe -i 123.pcm -ss 0 -t 59 1-123.wav
public String processCmd(String inputPath,String ffmpegPath,
String startTime,String length,String outputPath) {
List<String> commend = new java.util.ArrayList<String>();
commend.add(ffmpegPath);
commend.add("-i");
commend.add(inputPath);
commend.add("-ss");
commend.add(startTime);
commend.add("-t");
commend.add(length);
commend.add(outputPath);
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
builder.redirectErrorStream(true);
Process p = builder.start();
// 1. start
BufferedReader buf = null; // 保存ffmpeg的输出结果流
String line = null;
// read the standard output
buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuffer sb = new StringBuffer();
while ((line = buf.readLine()) != null) {
System.out.println(line);
sb.append(line);
continue;
}
p.waitFor();// 这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行
// 1. end
return sb.toString();
} catch (Exception e) {
System.out.println(e);
return null;
}
}
//ffmpeg -y -i 16k.wav -acodec pcm_s16le -f s16le -ac 1 -ar 16000 16k.pcm
public static String processWavToPcm(String inputPath,String ffmpegPath,String outputPath) {
List<String> commend = new java.util.ArrayList<String>();
commend.add(ffmpegPath);
commend.add("-i");
commend.add(inputPath);
commend.add("-acodec");
commend.add("pcm_s16le");
commend.add("-f");
commend.add("s16le");
commend.add("-ac");
commend.add("1");
commend.add("-ar");
commend.add("16000");
commend.add(outputPath);
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commend);
builder.redirectErrorStream(true);
Process p = builder.start();
// 1. start
BufferedReader buf = null; // 保存ffmpeg的输出结果流
String line = null;
// read the standard output
buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
StringBuffer sb = new StringBuffer();
while ((line = buf.readLine()) != null) {
System.out.println(line);
sb.append(line);
continue;
}
p.waitFor();// 这里线程阻塞,将等待外部转换进程运行成功运行结束后,才往下执行
// 1. end
return outputPath;
//sb.toString();
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
List<String> audios = new CutService().cutFile(
"E:\\QLDownload\\氧化还原反应中电子转移的方向和数目的表示方法\\氧化还原反应中电子转移的方向和数目的表示方法.wav",
"D:\\ffmpeg4.2\\bin\\ffmpeg.exe");
System.out.println(audios.size());
for (String wavPath : audios) {
String out = wavPath.substring(0,wavPath.lastIndexOf(".")) + ".pcm";
processWavToPcm(wavPath, "D:\\ffmpeg4.2\\bin\\ffmpeg.exe", out);
}
}
}
5、音频格式转换,便于进行语音识别,代码如上:
6、调用sdk,获取识别结果:
package com.my.ai.service;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.baidu.aip.speech.AipSpeech;
@Service
public class TokenService {
public static Logger logger = LoggerFactory.getLogger(TokenService.class);
//设置APPID/AK/SK
public static final String APP_ID = "***";
public static final String API_KEY = "***";
public static final String SECRET_KEY = "***";
static AipSpeech client = null;
static {
if(client == null) {
client = new AipSpeech(APP_ID, API_KEY, SECRET_KEY);
}
}
public static void main(String[] args) {
getResult("E:\\QLDownload\\氧化还原反应中电子转移的方向和数目的表示方法\\0-氧化还原反应中电子转移的方向和数目的表示方法.pcm");
}
public static String getResult(String file) {
// 可选:设置网络连接参数
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 可选:设置代理服务器地址, http和socket二选一,或者均不设置
//client.setHttpProxy("proxy_host", proxy_port); // 设置http代理
//client.setSocketProxy("proxy_host", proxy_port); // 设置socket代理
JSONObject res = client.asr(file, "pcm", 16000, null);
//System.out.println(res.toString(2));
System.out.println(res.get("result").toString());
return res.get("result").toString();
}
}
7、结果写入文件:
package com.my.ai.service;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class FileService {
public static Logger logger = LoggerFactory.getLogger(FileService.class);
//最慢
public static void writeFile1(String file,String content) throws IOException {
FileOutputStream out = null;
out = new FileOutputStream(new File(file));
long begin = System.currentTimeMillis();
out.write(content.getBytes());
out.close();
long end = System.currentTimeMillis();
System.out.println("FileOutputStream执行耗时:" + (end - begin) + " 毫秒");
}
//中
public static void writeFile2(String file,String content) throws IOException{
FileWriter fw = null;
fw = new FileWriter(file);
long begin3 = System.currentTimeMillis();
fw.write(content);
fw.close();
long end3 = System.currentTimeMillis();
System.out.println("FileWriter执行耗时:" + (end3 - begin3) + " 毫秒");
}
//最快
public static void writeFile3(String file,String content) throws IOException{
FileOutputStream outSTr = null;
BufferedOutputStream buff = null;
outSTr = new FileOutputStream(new File(file));
buff = new BufferedOutputStream(outSTr);
long begin0 = System.currentTimeMillis();
buff.write(content.getBytes());
buff.flush();
buff.close();
long end0 = System.currentTimeMillis();
System.out.println("BufferedOutputStream执行耗时:" + (end0 - begin0) + " 毫秒");
}
public static void main(String[] args) {
for (int i = 0; i < 7; i++) {
String result = TokenService.getResult("E:\\QLDownload\\氧化还原反应中电子转移的方向和数目的表示方法\\" + i +"-氧化还原反应中电子转移的方向和数目的表示方法.pcm");
appendFile2("E:\\QLDownload\\氧化还原反应中电子转移的方向和数目的表示方法\\氧化还原反应中电子转移的方向和数目的表示方法.txt", result+"\r\n");
}
}
public static void appendFile1(String file, String conent) {
BufferedWriter out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
out.write(conent);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 追加文件:使用FileWriter
*
* @param fileName
* @param content
*/
public static void appendFile2(String fileName, String content) {
FileWriter writer = null;
try {
// 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
writer = new FileWriter(fileName, true);
writer.write(content);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 追加文件:使用RandomAccessFile
*
* @param fileName 文件名
* @param content 追加的内容
*/
public static void appendFile3(String fileName, String content) {
RandomAccessFile randomFile = null;
try {
// 打开一个随机访问文件流,按读写方式
randomFile = new RandomAccessFile(fileName, "rw");
// 文件长度,字节数
long fileLength = randomFile.length();
// 将写文件指针移到文件尾。
randomFile.seek(fileLength);
randomFile.writeBytes(content);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (randomFile != null) {
try {
randomFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
8、测试:
package com.my.ai.test;
import java.util.List;
import com.my.ai.service.CutService;
import com.my.ai.service.ExtractAudioService;
import com.my.ai.service.FileService;
import com.my.ai.service.TokenService;
public class TestService {
public static void main(String[] args) {
ExtractAudioService audioService = new ExtractAudioService();
String outPath = audioService.getAudioFromVideo("G:\\Youku Files\\transcode\\化学高中必修1__第2章第3节·氧化还原反应_标清.mp4", "D:\\ffmpeg4.2\\bin\\ffmpeg.exe");
List<String> audios = new CutService().cutFile(outPath,"D:\\ffmpeg4.2\\bin\\ffmpeg.exe");
for (String wavPath : audios) {
String out = wavPath.substring(0,wavPath.lastIndexOf(".")) + ".pcm";
String outPcm = CutService.processWavToPcm(wavPath, "D:\\ffmpeg4.2\\bin\\ffmpeg.exe", out);
String result = TokenService.getResult(outPcm);
FileService.appendFile2("G:\\Youku Files\\transcode\\化学高中必修1__第2章第3节·氧化还原反应_标清.mp4-字幕.txt", result+"\r\n");
}
}
}
所有评论(0)