diff --git a/README.md b/README.md index 4cd00e8..9cedbc7 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Go go go, Bilibili Pikachu! | retryIfLiveOff | 否 | 当目标不在直播时,是否继续重试。默认false | | maxRetryIfLiveOff | 否 | 当目标不在直播时,继续重试的次数。默认0,此时会一直进行尝试,直到主播上线 | | retryAfterMinutes | 否 | 当目标不在直播时,每次获取直播间信息的时间间隔,单位分钟。默认`5.0` | - +| checkWithBuffer | 否 | 校准时间戳时是否使用缓存,默认true。测试功能,使用后性能有所提升 | + 各直播源解析情况 @@ -65,10 +65,12 @@ Go go go, Bilibili Pikachu! | huajiao | 2019/06/02 | `flv`只支持默认清晰度(似乎只有一种清晰度) | + 校正某FLV文件的时间戳 + + `java -Dfile.encoding=utf-8 -cp BiliLiveRecorder.jar nicelee.bilibili.live.check.FlvCheckerWithBuffer "源文件路径"` + `java -Dfile.encoding=utf-8 -cp BiliLiveRecorder.jar nicelee.bilibili.live.FlvChecker "源文件路径"` + `java -Dfile.encoding=utf-8 -cp BiliLiveRecorder.jar nicelee.bilibili.live.FlvChecker "源文件路径" true` + `java -Dfile.encoding=utf-8 -cp BiliLiveRecorder.jar nicelee.bilibili.live.FlvChecker "源文件路径" true false "保存的文件夹路径"` - + 第二个参数-布尔参数的意义是**当遇到某种特定情况时,是否分割文件** + + 两个校验工具使用方法类似,仅入口类不一致。功能稳定后将一直使用缓存减少IO + + 第二个参数-布尔参数的意义是**当遇到某种特定情况时,是否分割文件** + 第三个参数-布尔参数的意义是**是否输出debug信息** + 注意:这些操作**没法还原**,所以理论上原始文件最保真。 `不校验时间戳` ≈ `校验文件不分割` > `校验文件分割scripts tag` + 如果仍旧没办法满足需求的话,建议拿着各种版本都去ffmpeg处理一下 diff --git a/UPDATE.md b/UPDATE.md index f6f1734..f0ba8d5 100644 --- a/UPDATE.md +++ b/UPDATE.md @@ -1,4 +1,6 @@ ## 更新 ++ V2.6.3 + * 优化 #26 ,校正时间戳时可以使用缓存 + V2.6.2 * 优化 #25 ,以FlvChecker的Main方法运行时,接受debug布尔开关 * 修复一个bug #22 ,以FlvChecker的Main方法运行时,解析传入的文件保存路径; diff --git a/src/main/java/nicelee/bilibili/Main.java b/src/main/java/nicelee/bilibili/Main.java index 24bdb5e..a951316 100644 --- a/src/main/java/nicelee/bilibili/Main.java +++ b/src/main/java/nicelee/bilibili/Main.java @@ -18,6 +18,7 @@ import nicelee.bilibili.enums.StatusEnum; import nicelee.bilibili.live.FlvChecker; import nicelee.bilibili.live.RoomDealer; +import nicelee.bilibili.live.check.FlvCheckerWithBuffer; import nicelee.bilibili.live.domain.RoomInfo; import nicelee.bilibili.util.Logger; import nicelee.bilibili.util.TrustAllCertSSLUtil; @@ -25,10 +26,11 @@ public class Main { - final static String version = "v2.6.2"; + final static String version = "v2.6.3"; static boolean autoCheck; static boolean splitScriptTagsIfCheck; static boolean deleteOnchecked; + static boolean flvCheckWithBuffer; static boolean flagZip; static String liver; static String shortId; @@ -81,12 +83,17 @@ public static void main(String[] args) throws IOException { retryIfLiveOff = false; maxRetryIfLiveOff = 0; retryAfterMinutes = 5; + flvCheckWithBuffer = true; // 根据参数初始化值 if (args != null && args.length >= 1) { String value = getValue(args[0], "check"); if ("false".equals(value)) { autoCheck = false; } + value = getValue(args[0], "checkWithBuffer"); + if ("false".equals(value)) { + flvCheckWithBuffer = false; + } value = getValue(args[0], "splitScriptTags"); if ("true".equals(value)) { splitScriptTagsIfCheck = true; @@ -331,7 +338,10 @@ public void run() { try { for (String path : fileList) { System.out.println("校对时间戳开始..."); - new FlvChecker().check(path, deleteOnchecked, splitScriptTagsIfCheck, saveFolderAfterCheck); + if(flvCheckWithBuffer) + new FlvCheckerWithBuffer().check(path, deleteOnchecked, splitScriptTagsIfCheck, saveFolderAfterCheck); + else + new FlvChecker().check(path, deleteOnchecked, splitScriptTagsIfCheck, saveFolderAfterCheck); System.out.println("校对时间戳完毕。"); } } catch (IOException e) { diff --git a/src/main/java/nicelee/bilibili/live/check/FlvCheckerWithBuffer.java b/src/main/java/nicelee/bilibili/live/check/FlvCheckerWithBuffer.java new file mode 100644 index 0000000..3147da0 --- /dev/null +++ b/src/main/java/nicelee/bilibili/live/check/FlvCheckerWithBuffer.java @@ -0,0 +1,372 @@ +package nicelee.bilibili.live.check; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import nicelee.bilibili.util.Logger; + +public class FlvCheckerWithBuffer { + + public static void main(String[] args) throws IOException { +// args = new String[] {"D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\虎牙-原始样本1.flv" }; +// args = new String[] {"D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\斗鱼-原始样本1.flv", "true", "false" }; +// args = new String[] {"D:\\Workspace\\测试douyu-余霜Yscandice-2020-01-16 13.41-2020-01-16 13.42-0.flv", "true", "true" }; +// args = new String[] {"D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\B站-原始样本1.flv" }; +// args = new String[] {"D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\快手-原始样本1.flv", "true", "false" }; +// args = new String[] {"D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\快手-原始样本2.flv" }; + + FlvCheckerWithBuffer fChecker = new FlvCheckerWithBuffer(); + boolean splitScripts = false; + String saveFolder = null; + if(args != null) { + if(args.length >= 4) { + saveFolder = args[3]; + File f = new File(saveFolder); + if(!f.exists()) + f.mkdirs(); + } + if(args.length >= 3 && "false".equals(args[2])) + Logger.debug = false; + + if(args.length >= 2) + splitScripts = "true".equals(args[1]); + + if (args.length >= 1) { + System.out.println("校对时间戳开始..."); + fChecker.check(args[0], false, splitScripts, saveFolder); + System.out.println("校对时间戳完毕。"); + } else { + System.out.println("请输入正确的文件路径"); + } + } + } + + /** + * 从头部开始Check, 重新锚定时间戳, 将最后一帧(不管是否完整)去掉 + * + * @param path + * @throws IOException + */ + // 用于统计时间戳 + private int lastTimestampRead[] = { -1, -1 }, lastTimestampWrite[] = { -1, -1 }; + // 用于缓冲 + private static byte[] buffer = new byte[1024 * 1024 * 8]; + + public void check(String path) throws IOException { + check(path, false, false); + } + public void check(String path, boolean deleteOnchecked) throws IOException { + check(path, deleteOnchecked, false); + } + + public void check(String path, boolean deleteOnchecked, boolean splitScripts) throws IOException { + check(path, deleteOnchecked, splitScripts, null); + } + + public void check(String path, boolean deleteOnchecked, boolean splitScripts, String saveFolder) throws IOException { + Logger.println("校对时间戳开始..."); + File file = new File(path); + RafRBuffered raf = new RafRBuffered(file, "r"); + + File destFolder = null; + if(saveFolder != null) { + destFolder = new File(saveFolder); + }else { + destFolder = file.getParentFile(); + } + File fileNew = null; + Pattern pattern = Pattern.compile("-checked([0-9]+).flv$"); + Matcher matcher = pattern.matcher(file.getName()); + if (matcher.find()) { + int index = Integer.parseInt(matcher.group(1)); + index++; + fileNew = new File(destFolder, file.getName().replaceFirst("[0-9]+.flv$", index + ".flv")); + } else { + fileNew = new File(destFolder, file.getName().replaceFirst(".flv$", "-checked0.flv")); + } + RafWBuffered rafNew = new RafWBuffered(fileNew, "rw"); + // 复制头部 + raf.read(buffer, 0, 9); + rafNew.write(buffer, 0, 9); + // 处理Tag内容 + checkTag(raf, rafNew, fileNew, splitScripts); + + raf.close(); + rafNew.close(); + changeDuration(fileNew.getAbsolutePath(), this.getDuration() / 1000); + if (deleteOnchecked) { + file.delete(); + } + } + + /** + * @param raf + * @param rafNew + */ + private void checkTag(RafRBuffered raf, RafWBuffered rafNew, File fileNew, boolean splitScripts) { + // 用于排除无效尾巴帧 + long currentLength = 9L, latsValidLength = currentLength; + try { + int remain = 40; + boolean isFirstScriptTag = true; + int timestamp = 0; + while (true) {// && remain>=0 + remain--; + // 读取前一个tag size + int predataSize = readBytesToInt(raf, 4); + //System.out.println("前一个 tagSize:" + predataSize); + rafNew.write(buffer, 0, 4); + // 记录当前新文件位置,若下一tag无效,则需要回退 + latsValidLength = currentLength; + currentLength = rafNew.getFilePointer(); + // Logger.print("前一个长度为:" + predataSize); + + // 读取tag + // tag 类型 + int tagType = raf.read(); + Logger.print("当前tag 类型为:" + tagType); + if (tagType == 8 || tagType == 9) {// 8/9 audio/video + rafNew.write(tagType); + // tag data size 3个字节。表示tag data的长度。从streamd id 后算起。 + int dataSize = readBytesToInt(raf, 3); + rafNew.write(buffer, 0, 3); + // Logger.print(" ,当前tag data 长度为:" + dataSize); + // 时间戳 3 + timestamp = readBytesToInt(raf, 3); + int timestampEx = raf.read() << 24; + timestamp += timestampEx; + dealTimestamp(rafNew, timestamp, tagType - 8); + raf.read(buffer, 0, 3 + dataSize); + rafNew.write(buffer, 0, 3 + dataSize); + + } else if (tagType == 18) { // 18 scripts + Logger.println("scripts"); + if (!splitScripts || isFirstScriptTag) { + // 如果是scripts脚本,默认为第一个tag,此时将前一个tag Size 置零 + rafNew.seek(rafNew.getFilePointer() -4); + byte[] zeroTimestamp = new byte[] { 0, 0, 0, 0 }; + rafNew.write(zeroTimestamp); + rafNew.write(tagType); + isFirstScriptTag = false; + + int dataSize = readBytesToInt(raf, 3); + rafNew.write(buffer, 0, 3); + Logger.println(" 当前tag data 长度为:" + dataSize); + + raf.skipBytes(4); + byte[] zeros = new byte[] { 0, 0, 0 }; + rafNew.write(zeros); // 时间戳 0 + rafNew.write(0); // 时间戳扩展 0 + raf.read(buffer, 0, 3 + dataSize); + rafNew.write(buffer, 0, 3 + dataSize); + }else { + Logger.println("第二个scripts脚本"); + // 当有第二个scripts脚本时 + // 1. 从第二个script tag起始新创建一份文件 + File fileNew2 = null; + Pattern pattern = Pattern.compile("-checked([0-9]+).flv$"); + Matcher matcher = pattern.matcher(fileNew.getName()); + if (matcher.find()) { + int index = Integer.parseInt(matcher.group(1)); + index++; + fileNew2 = new File(fileNew.getParentFile(), + fileNew.getName().replaceFirst("[0-9]+.flv$", index + ".flv")); + } else { + fileNew2 = new File(fileNew.getParentFile(), + fileNew.getName().replaceFirst(".flv$", "-checked0.flv")); + } + RafWBuffered rafNew2 = new RafWBuffered(fileNew2, "rw"); + // 记录当前位置 + long pos = raf.getFilePointer(); + // 复制 header + byte[] header = new byte[9]; + raf.seek(0); + raf.read(header); + rafNew2.write(header); + // 恢复位置 + raf.seek(pos - 5); + // 2. 处理新文件 + FlvCheckerWithBuffer fc = new FlvCheckerWithBuffer(); + fc.checkTag(raf, rafNew2, fileNew2, splitScripts); + // 3. 收尾并处理时长 + rafNew2.close(); + changeDuration(fileNew2.getAbsolutePath(), fc.getDuration() / 1000); + } + } else { + Logger.print("未知类型"); + Logger.print(tagType); + rafNew.setLength(latsValidLength); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + Logger.println(); + Logger.println("lastTimestamp 为:" + lastTimestampWrite[0]); + Logger.println("currentLength 为:" + currentLength); + } + + /** + * 处理音/视频时间戳 + * + * @param raf + * @param timestamp + * @throws IOException + * @return 是否忽略该tag + */ + private boolean dealTimestamp(RafWBuffered raf, int timestamp, int tagType) throws IOException { + Logger.print("上一帧读取timestamps 为:" + lastTimestampRead[tagType]); + Logger.print("上一帧写入timestamps 为:" + lastTimestampWrite[tagType]); + + // 如果是首帧 + if (lastTimestampRead[tagType] == -1) { + lastTimestampWrite[tagType] = 0; + } else if (timestamp >= lastTimestampRead[tagType]) {// 如果时序正常 + // 间隔十分巨大(1s),那么重新开始即可 + if (timestamp > lastTimestampRead[tagType] + 1000) { + lastTimestampWrite[tagType] += 10; + Logger.print("---"); + } else { + lastTimestampWrite[tagType] = timestamp - lastTimestampRead[tagType] + lastTimestampWrite[tagType]; + } + } else {// 如果出现倒序时间戳 + // 如果间隔不大,那么如实反馈 + if (lastTimestampRead[tagType] - timestamp < 5 * 1000) { + int tmp = timestamp - lastTimestampRead[tagType] + lastTimestampWrite[tagType]; + tmp = tmp > 0 ? tmp : 1; + lastTimestampWrite[tagType] = tmp; + } else {// 间隔十分巨大,那么重新开始即可 + lastTimestampWrite[tagType] += 10; + Logger.print("---rewind"); + } + } + lastTimestampRead[tagType] = timestamp; + + // 低于0xffffff部分 + int lowCurrenttime = lastTimestampWrite[tagType] & 0xffffff; + raf.write(int2Bytes(lowCurrenttime), 1, 3); + // 高于0xffffff部分 + int highCurrenttime = lastTimestampWrite[tagType] >> 24; + raf.write(highCurrenttime); + Logger.print(" ,读取timestamps 为:" + timestamp); + Logger.print(" ,写入timestamps 为:" + lastTimestampWrite[tagType]); + Logger.println(); + return true; + } + + + /** + * @param raf + * @param byteLength + * @return + * @throws IOException + */ + private int readBytesToInt(RafRBuffered raf, int byteLength) throws IOException { + raf.read(buffer, 0, byteLength); + return bytes2Int(buffer, byteLength); + } + + private byte[] int2Bytes(int value) { + byte[] byteRet = new byte[4]; + for (int i = 0; i < 4; i++) { + byteRet[3 - i] = (byte) ((value >> 8 * i) & 0xff); + // Logger.printf("%x ",byteRet[3-i]); + } + return byteRet; + } + + private int bytes2Int(byte[] bytes, int byteLength) { + int result = 0; + for (int i = 0; i < byteLength; i++) { + result |= ((bytes[byteLength - 1 - i] & 0xff) << (i * 8)); + // System.out.printf("%x ",(bytes[i] & 0xff)); + } + return result; + } + + private byte[] double2Bytes(double d) { + long value = Double.doubleToRawLongBits(d); + byte[] byteRet = new byte[8]; + for (int i = 0; i < 8; i++) { + byteRet[i] = (byte) ((value >> 8 * i) & 0xff); + } + byte[] byteReverse = new byte[8]; + for (int i = 0; i < 8; i++) { + byteReverse[i] = byteRet[7 - i]; + // System.out.printf("%x ",byteReverse[i]); + } + Logger.println(); + return byteReverse; + } + + public double bytes2Double(byte[] arr) { + byte[] byteReverse = new byte[8]; + for (int i = 0; i < 8; i++) { + byteReverse[i] = arr[7 - i]; + } + + long value = 0; + for (int i = 0; i < 8; i++) { + value |= ((long) (byteReverse[i] & 0xff)) << (8 * i); + } + return Double.longBitsToDouble(value); + } + + byte[] durationHeader = { 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e }; + int pDurationHeader = 0; + + public void changeDuration(String path, double duration) throws IOException { + // 08 64 75 72 61 74 69 6f 6e duration + // 00 bytes x8 + File file = new File(path); + RandomAccessFile raf = new RandomAccessFile(file, "rw"); + + int lenRead = raf.read(buffer); + int pDuration = checkBufferForDuration(); + boolean findDuration = false; + while (lenRead > -1) { + long offset = 0; + if (pDuration != -1) { + findDuration = true; + raf.seek(offset + pDuration + 1); + raf.write(0x00); + raf.write(double2Bytes(duration)); + break; + } + // Logger.println("当前完成度: " + cnt*100/total + "%"); + lenRead = raf.read(buffer); + if (!findDuration) { + pDuration = checkBufferForDuration(); + } + offset += lenRead; + } + raf.close(); + + } + + /** + * 检查buffer 是否包含duration头部 + * + * @return duration末尾在 buffer中的位置 + */ + int checkBufferForDuration() { + for (int i = 0; i < buffer.length; i++) { + if (buffer[i] == durationHeader[pDurationHeader]) { + pDurationHeader++; + if (pDurationHeader == durationHeader.length) { + pDurationHeader = 0; + return i; + } + } + } + return -1; + } + + public double getDuration() { + return (double) lastTimestampWrite[0]; + } +} diff --git a/src/main/java/nicelee/bilibili/live/check/RafRBuffered.java b/src/main/java/nicelee/bilibili/live/check/RafRBuffered.java new file mode 100644 index 0000000..fb90891 --- /dev/null +++ b/src/main/java/nicelee/bilibili/live/check/RafRBuffered.java @@ -0,0 +1,140 @@ +package nicelee.bilibili.live.check; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +public class RafRBuffered { + + byte buffer[]; + int pointer; + int validBound; + RandomAccessFile raf; + + public RafRBuffered(File file, String mode) throws FileNotFoundException { + this(file, mode, 1024 * 1024 * 4); + } + + public RafRBuffered(File file, String mode, int buffSize) throws FileNotFoundException { + buffer = new byte[buffSize]; + raf = new RandomAccessFile(file, mode); + pointer = 0; + validBound = 0; + } + + public int read() throws IOException { + if (pointer < validBound) { + int result = buffer[pointer]; + pointer++; + return result; + } else { + validBound = raf.read(buffer); + // System.out.println("I/O read ---"); + pointer = 1; + return buffer[0]; + } + } + + public int read(byte[] b) throws IOException { + return this.read(b, 0, b.length); + } + + public int read(byte[] b, int off, int len) throws IOException { + int bufferRemainSize = validBound - pointer; + // 若buffer里面内容充足,则直接从缓存读取 + if (bufferRemainSize >= len) { + System.arraycopy(buffer, pointer, b, off, len); + pointer += len; + return len; + } else { + // buffer里面内容不足,先将缓存里的全部读取 + System.arraycopy(buffer, pointer, b, off, bufferRemainSize); + int remainToRead = len - bufferRemainSize; + // 若要读的还剩很多,直接读取,否则先读入缓存 + if (remainToRead >= buffer.length) { + int readSize = raf.read(b, off + bufferRemainSize, remainToRead); +// System.out.println("I/O read ---"); + pointer = 0; + validBound = 0; + return bufferRemainSize + readSize; + } else { + int readSize = raf.read(buffer); +// System.out.println("I/O read ---"); + if(readSize == -1) { + pointer = 0; + validBound = 0; + if(bufferRemainSize == 0) + return -1; + else + return bufferRemainSize; + } + + if (readSize >= remainToRead) { + System.arraycopy(buffer, 0, b, off + bufferRemainSize, remainToRead); + validBound = readSize; + pointer = remainToRead; + return bufferRemainSize + remainToRead; + } else { + System.arraycopy(buffer, 0, b, off + bufferRemainSize, readSize); + pointer = 0; + validBound = 0; + return bufferRemainSize + readSize; + } + } + } + + } + + public int skipBytes(int n) throws IOException { + int bufferRemainSize = validBound - pointer; + // 若buffer里面内容充足,则直接从缓存跳过 + if (bufferRemainSize >= n) { + pointer += n; + return n; + } else { + // buffer里面内容不足,先将缓存里的全部跳过 + int remainToRead = n - bufferRemainSize; + // 若要跳过的还剩很多,直接跳过,否则先读入缓存再跳过 + if (remainToRead >= buffer.length) { + int readSize = raf.skipBytes(remainToRead); +// System.out.println("I/O skip ---"); + pointer = 0; + validBound = 0; + return bufferRemainSize + readSize; + } else { + int readSize = raf.read(buffer); +// System.out.println("I/O read ---"); + + if (readSize >= remainToRead) { + validBound = readSize; + pointer = remainToRead; + return bufferRemainSize + remainToRead; + } else { + pointer = 0; + validBound = 0; + return bufferRemainSize + readSize; + } + } + } + } + + public void seek(long pos) throws IOException { + long min = raf.getFilePointer() - validBound; + long max = raf.getFilePointer(); + if (pos >= min && pos < max) { + pointer = (int) (pos - min); + } else { + raf.seek(pos); + } + } + + public void close() throws IOException { + raf.close(); + } + + public long getFilePointer() throws IOException { + return raf.getFilePointer() - validBound + pointer; + } + +} diff --git a/src/main/java/nicelee/bilibili/live/check/RafWBuffered.java b/src/main/java/nicelee/bilibili/live/check/RafWBuffered.java new file mode 100644 index 0000000..4498f27 --- /dev/null +++ b/src/main/java/nicelee/bilibili/live/check/RafWBuffered.java @@ -0,0 +1,98 @@ +package nicelee.bilibili.live.check; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +public class RafWBuffered { + + byte buffer[]; + int pointer; + RandomAccessFile raf; + + public RafWBuffered(File file, String mode) throws FileNotFoundException { + this(file, mode, 1024 * 1024 * 4); + } + + public RafWBuffered(File file, String mode, int buffSize) throws FileNotFoundException { + buffer = new byte[buffSize]; + raf = new RandomAccessFile(file, mode); + pointer = 0; + } + + public void write(int b) throws IOException { + if (pointer == buffer.length) { + raf.write(buffer); + buffer[0] = (byte) (b & 0xff); + pointer = 1; + } else { + buffer[pointer] = (byte) (b & 0xff); + pointer++; + } + } + + public void write(byte[] b) throws IOException { + this.write(b, 0, b.length); + } + + public void write(byte[] b, int off, int len) throws IOException { + int bufferRemainSize = buffer.length - pointer; + if (bufferRemainSize >= len) { + // 若buffer剩余空间足够大,那么直接写入缓存 + System.arraycopy(b, off, buffer, pointer, len); + pointer += len; + } else { + // 如果buffer空间不够,那么先写满buffer后写入文件 + System.arraycopy(b, off, buffer, pointer, bufferRemainSize); + raf.write(buffer); + // 如果剩下的依然大于空buffer大小,那么直接写入文件,否则写入缓存 + int remain = len - bufferRemainSize; + if (remain > buffer.length) { + raf.write(b, off + bufferRemainSize, remain); + pointer = 0; + } else { + System.arraycopy(b, off + bufferRemainSize, buffer, 0, remain); + pointer = remain; + } + } + } + + /** + * 该方法会执行buffer清空操作,需减少使用次数 + * + * @param pos + * @throws IOException + */ + public void seek(long pos) throws IOException { + this.flush(); + raf.seek(pos); + } + + public void close() throws IOException { + this.flush(); + raf.close(); + } + + /** + * 该方法会执行buffer清空操作,需减少使用次数 + * + * @param newLength + * @throws IOException + */ + public void setLength(long newLength) throws IOException { + this.flush(); + raf.setLength(newLength); + } + + public long getFilePointer() throws IOException { + return raf.getFilePointer() + pointer; + } + + public void flush() throws IOException { + if (pointer > 0) { + raf.write(buffer, 0, pointer); + pointer = 0; + } + } +} diff --git a/src/test/java/nicelee/test/junit/FLVCheckTest.java b/src/test/java/nicelee/test/junit/FLVCheckTest.java new file mode 100644 index 0000000..110d067 --- /dev/null +++ b/src/test/java/nicelee/test/junit/FLVCheckTest.java @@ -0,0 +1,58 @@ +package nicelee.test.junit; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import nicelee.bilibili.live.FlvChecker; +import nicelee.bilibili.live.check.FlvCheckerWithBuffer; +import nicelee.bilibili.util.Logger; + +public class FLVCheckTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testCheckSpeed() { + String path = "D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\斗鱼-原始样本2.flv"; + try { + Logger.debug = false; + long begin1 = System.currentTimeMillis(); + new FlvChecker().check(path, false, true, null); + long end1 = System.currentTimeMillis(); + long begin2 = System.currentTimeMillis(); + new FlvCheckerWithBuffer().check(path, false, true, null); + long end2 = System.currentTimeMillis(); + + System.out.println("不使用缓存花费ms:" + (end1 - begin1)); + System.out.println("使用缓存花费ms:"+ (end2 - begin2)); + } catch (IOException e) { + e.printStackTrace(); + } + } + +// @Test + public void testCheckValid() { + String path = "D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\斗鱼-原始样本2.flv"; +// String path = "D:\\Workspace\\javaweb-springboot\\BilibiliLiveRecord\\download\\样本\\斗鱼-原始样本2-checked0.flv"; + try { + new FlvChecker().checkFromEnd(path); + } catch (IOException e) { + e.printStackTrace(); + } + } + + +}