Ogg Opus granule position转时间戳实现文件无损裁剪问题咨询
Ogg Opus无转码裁剪判断逻辑实现
核心前提
Opus在Ogg封装中的时间计量基于48kHz固定采样率,你可以先将裁剪时间窗口转换为采样计数基准值:
// 成员变量,提前初始化 private long startSample; private long endSample; // 初始化时调用一次,将毫秒转为48kHz采样数 public void initTrimRange(long startTimeMs, long endTimeMs) { this.startSample = startTimeMs * 48L; // 48000采样率 / 1000ms = 48采样/ms this.endSample = endTimeMs * 48L; }
方案1:基于Ogg页粒度判断(实现pageLiesWithinTrimRange)
Ogg页的granulePosition字段定义为该页内最后一个完整音频包解码后的结束采样位置,你需要额外增加一个成员变量记录上一个音频页的结束采样位置:
// 成员变量,记录上一页的granulePosition,初始值为0 private long prevPageGranule = 0L; private boolean pageLiesWithinTrimRange(OggPage page){ long currentGranule = page.getGranulePosition(); // 跳过ID头、评论头等头页面,你已经单独写入输出流 if (page.isHeaderPage() || currentGranule == 0) { prevPageGranule = currentGranule; return false; } // 当前页覆盖的采样区间为 [prevPageGranule + 1, currentGranule] // 判断区间是否和裁剪窗口有交集 boolean isInRange = (prevPageGranule + 1 <= endSample) && (currentGranule >= startSample); // 更新上一页granule记录 prevPageGranule = currentGranule; return isInRange; }
方案2:基于音频包粒度判断(实现packetLiesWithinTrimRange)
包粒度判断精度更高,需要额外记录当前已处理的累计采样偏移:
// 成员变量,记录当前累计处理的采样偏移,初始值为0 private long currentSampleOffset = 0L; private boolean packetLiesWithinTrimRange(OpusPacket packet){ // 从Opus包中获取该包的采样数,oggus库已封装该能力 long packetSampleCount = packet.getSampleCount(); long packetStart = currentSampleOffset; long packetEnd = currentSampleOffset + packetSampleCount - 1; // 更新累计偏移 currentSampleOffset += packetSampleCount; // 判断包的采样区间是否和裁剪窗口有交集 return (packetStart <= endSample) && (packetEnd >= startSample); }
注意事项
- 裁剪后输出的第一个音频页的
granulePosition需要修正,减去裁剪起始点的采样值,保证输出文件的时间轴从0开始 - 输出的所有Ogg页的
serialNum需要保持统一 - 输出流开头需要先写入ID头和评论头,再写入过滤后的音频页
内容的提问来源于stack exchange,提问作者Omkar T
相关产品推荐
相关产品推荐

