Android Studio使用mp4parser合并视频出现黑屏有声音问题求助
嘿,我之前也碰到过mp4parser拼接视频时黑屏但声音正常的情况,和你说的一模一样——同一段视频拼接完全没问题,换不同视频就翻车。大概率是两个视频的视频轨道参数不匹配导致的,比如分辨率、帧率、编码格式(甚至H.264的profile/level)不一样,播放器没法正确解析拼接后的视频帧,就只能出声音黑屏了。
先给你拆解下问题,再给几个可行的解决方案:
第一步:排查视频轨道参数是否一致
首先得确认两个视频的视频轨道参数是否完全匹配,这是最核心的问题。你可以在代码里加几行打印,快速查看关键参数:
for (Movie m : inMovies) { for (Track t : m.getTracks()) { if (t.getHandler().equals("soun")) { audioTracks.add(t); } if (t.getHandler().equals("vide")) { videoTracks.add(t); // 打印关键参数,方便排查 System.out.println("视频分辨率: " + t.getWidth() + "x" + t.getHeight()); VisualSampleEntry visualEntry = (VisualSampleEntry) t.getSampleDescriptionBox().getBoxes().get(0); System.out.println("编码格式: " + visualEntry.getCodec()); System.out.println("时间基准(timescale): " + t.getTimescale()); } } }
如果输出里两个视频的分辨率、编码格式或者timescale不一样,那就是问题根源了。
第二步:针对参数不匹配的解决方案
方案1:统一视频参数后再拼接
mp4parser本身不支持转码,所以你需要先把第二个视频转成和第一个视频参数完全一致的MP4,再用你的原有代码拼接。在Android上可以用MediaCodec实现转码,或者选择避免了text relocations问题的轻量转码库。
方案2:手动统一视频轨道的SampleDescription(仅兼容场景可用)
如果两个视频的编码格式高度兼容(比如都是H.264,只是细微参数不同),可以尝试把第二个视频轨道的SampleDescription替换成第一个的,让播放器用统一的参数解析所有帧。修改你代码里添加视频轨道的部分:
if (videoTracks.size() > 0) { // 取第一个视频轨道作为基准,复用它的SampleDescription Track baseVideoTrack = videoTracks.get(0); List<Track> adjustedVideoTracks = new LinkedList<>(); adjustedVideoTracks.add(baseVideoTrack); for (int i = 1; i < videoTracks.size(); i++) { Track currentTrack = videoTracks.get(i); // 创建自定义Track,替换SampleDescription为基准轨道的 Track adjustedTrack = new Track() { @Override public List<Sample> getSamples() { return currentTrack.getSamples(); } @Override public SampleDescriptionBox getSampleDescriptionBox() { return baseVideoTrack.getSampleDescriptionBox(); } @Override public String getHandler() { return currentTrack.getHandler(); } @Override public List<Edit> getEdits() { return currentTrack.getEdits(); } @Override public long getDuration() { return currentTrack.getDuration(); } @Override public long getTimescale() { return currentTrack.getTimescale(); } @Override public String getName() { return currentTrack.getName(); } @Override public Type getType() { return currentTrack.getType(); } }; adjustedVideoTracks.add(adjustedTrack); } // 用调整后的轨道拼接 result.addTrack(new AppendTrack(adjustedVideoTracks.toArray(new Track[adjustedVideoTracks.size()]))); }
注意:这个方法只适用于编码格式高度兼容的情况,如果分辨率差异大,可能会出现画面拉伸或者依然黑屏的情况。
方案3:处理时间基准(timescale)不匹配的问题
如果两个视频的timescale不一样,拼接后的时间轴会混乱,也可能导致黑屏。你需要把第二个轨道的timescale调整成和第一个一致,同时按比例修改duration:
// 在方案2的基础上,添加timescale调整 long baseTimescale = baseVideoTrack.getTimescale(); long currentTimescale = currentTrack.getTimescale(); if (currentTimescale != baseTimescale) { long adjustedDuration = (currentTrack.getDuration() * baseTimescale) / currentTimescale; // 修改自定义Track的duration和timescale Track adjustedTrack = new Track() { // ... 其他方法不变 @Override public long getDuration() { return adjustedDuration; } @Override public long getTimescale() { return baseTimescale; } }; adjustedVideoTracks.add(adjustedTrack); }
额外检查:轨道的Edit列表
有些视频轨道会包含Edit信息(比如用于裁剪、变速),如果两个视频的Edit列表不一致,也可能导致播放异常。你可以在代码里打印currentTrack.getEdits(),如果不是空的,可能需要统一处理或者清空Edit列表。
先试试第一步的参数排查,大部分情况下都是参数不匹配的问题,解决了这个应该就能正常播放了。
内容的提问来源于stack exchange,提问作者Darkhmar




