使用FFmpeg进行视频抽取音频,之后进行语音识别转为文字
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
bulid.gradle
工程组件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;
}
} }
ExtractAudioService
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");
}
} }
使用FFmpeg进行视频抽取音频,之后进行语音识别转为文字的更多相关文章
- ffmpeg实现视频文件合并/截取预览视频/抽取音频/crop(裁剪)(ffmpeg4.2.2)
一,ffmpeg的安装 请参见: https://www.cnblogs.com/architectforest/p/12807683.html 说明:刘宏缔的架构森林是一个专注架构的博客,地址:ht ...
- 在java中使用FFmpeg处理视频与音频
FFmpeg是一个非常好用的视频处理工具,下面讲讲如何在java中使用该工具类. 一.首先,让我们来认识一下FFmpeg在Dos界面的常见操作 1.拷贝视频,并指定新的视频的名字以及格式 ffmpeg ...
- FFmpeg开发实战(四):FFmpeg 抽取音视频的音频数据
如何使用FFmpeg抽取音视频的音频数据,代码如下: void adts_header(char *szAdtsHeader, int dataLen); // 使用FFmpeg从视频中抽取音频 vo ...
- FFmpeg进行视频帧提取&音频重采样-Process.waitFor()引发的阻塞超时
由于产品需要对视频做一系列的解析操作,利用FFmpeg命令来完成视频的音频提取.第一帧提取作为封面图片.音频重采样.字幕压缩等功能: 前一篇文章已经记录了FFmpeg在JAVA中的使用-音频提取&am ...
- Java使用FFmpeg处理视频文件指南
Java使用FFmpeg处理视频文件指南 本文主要讲述如何使用Java + FFmpeg实现对视频文件的信息提取.码率压缩.分辨率转换等功能: 之前在网上浏览了一大圈Java使用FFmpeg处理音视频 ...
- Java使用FFmpeg处理视频文件的方法教程
这篇文章主要给大家介绍了关于Java使用FFmpeg处理视频文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧 前言 本文主要 ...
- ffmpeg为视频添加时间戳 - 手动编译ffmpeg
FFMPEG给视频加时间戳水印 项目中需要给视频添加时间戳,理所当然最好用的办法是ffmpeg.在找到正确的做法前,还被网上的答案timecode给水了一下(水的不轻,在这里转了2天),大概是这样写的 ...
- 使用ffmpeg 对视频截图,和视频转换格式
//执行CMD命令方法 public static void CmdProcess(string command)//调用CMD { //实例化一个进程类 ...
- NET 2.0(C#)调用ffmpeg处理视频的方法
另外:ffmpeg的net封装库 http://www.intuitive.sk/fflib/ NET 2.0 调用FFMPEG,并异步读取输出信息的代码...public void ConvertV ...
随机推荐
- EntityFramework - Code First - 数据迁移
需求 在更新模型之后同步更新数据库里的表,并不丢失原有数据 使用默认值填充新增加的字段 EntityFramework迁移命令 Enable-Migrations 启用迁移 Add-Migration ...
- [C++] NEW Advanced Usage
NEW Advanced Usage 将分配的内存限定在特定的一块区域 #include<iostream> #include<new> ); ); }; using name ...
- [C++] Pen questions & linux cmd
1.宏替换,完全展开替换,注意带来副作用 #include <stdio.h>#define 打印语句 printf(“hello”); Void main(void) { If (1) ...
- code1796 社交网络
输入描述 Input Description 输入文件中第一行有两个整数,n 和 m,表示社交网络中结点和无向边的数 目.在无向图中,我们将所有结点从 1 到 n 进行编号. 接下来 m 行,每行用三 ...
- Web测试实践-任务进度-Day01
任务安排 说明:小组全体成员都参与了会议,对该实践进行分析以及对实践任务的拆分以及进行了任务的分配. 小组成员 华同学.郭同学.覃同学.刘同学.穆同学.沈同学 阶段划分 阶段1:评测被测系统 1.对被 ...
- QML开发常见错误(原)
大部分错误,都是因为没有重新编译或者清理导致的.消除步骤: 先排除基本语法错误 清理项目 如果前两步都没有效果,手动删除程序生成目录 1.添加新控件,运行时不识别,如 qrc:ui/main.qml: ...
- 【转载】rabbitmq的发布确认和事务
地址:https://my.oschina.net/lzhaoqiang/blog/670749 摘要: 介绍confirm的工作机制.使用spring-amqp介绍事务以及发布确认的使用方式.因为事 ...
- C# 静态类的使用
静态类与非静态类基本相同,但存在一个区别:静态类不能实例化.也就是说,不能使用 new 关键字创建静态类类型的变量.因为没有实例变量,所以要使用类名本身访问静态类的成员. static class C ...
- 大型Unity手游《英雄之刃-最后之战》源码分析
英雄之刃之最后一战是国内首款原创精品MOBA手游,是一款由前暴雪文案亲自操刀世界观,日韩专业团队打造美术场景,新加坡团队精心制作战斗音乐的旷世之作! 超快速的匹配对战.默契的团队协作给你带来意犹未尽的 ...
- Tomcat version 6.0 only supports J2EE 1.2 ......
在project的.setting folder下面,有个名为org.eclipse.wst.common.project.facet.core.xml的文件,里面配置有各种版本信息.此时,按照本机配 ...