ffmpeg java 实时视频流转码

开源地址 https://github.com/bramp/ffmpeg-cli-wrapper

由于java部分关于ffmpeg的代码处理较少,本来是开子线程通过模拟命令行环境输入命令来实时视频流转码及视频流截取保存文件,由于无法在保存文件之后关闭线程,后使用该开源组件重构(虽然也是模拟命令行的方式,但是在线程的处理上会比自己手写更加精细),开贴记录下使用过程

pom导入及windows 安装包(可以通过将运行源文件放在项目中模拟运行环境)请自行下载
在这里插入图片描述
视频流从rtsp转为rtmp

拼接出的命令为:ffmpeg -i rtmp://172.16.10.33:1935/hls/peddler -vcodec copy -f flv -an rtmp://172.16.10.16:1935/hls/1

public class ConvertCodeTest {
    private static Map<Integer, FFmpegJob> map = new ConcurrentHashMap<>(8);

    public static void main(String[] args) throws IOException, URISyntaxException {
        FFmpeg ffmpeg = new FFmpeg("C:/ffmpegCommandHandlerService/src/main/java/com/highsai/app/ffmpeg/ffmpeg.exe");
        FFprobe ffprobe = new FFprobe("C:/ffmpegCommandHandlerService/src/main/java/com/highsai/app/ffmpeg/ffprobe.exe");
        FFmpegOutputBuilder outputBuilder = new FFmpegOutputBuilder()
            .setUri(new URI("rtmp://172.16.10.16:1935/hls/1"))
            .disableAudio()
            .setVideoCodec("copy")
            .setFormat("flv");
        FFmpegBuilder builder = new GkFFmpegBuilder ()
            .setInput("rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov")
//            .overrideOutputFiles(true) // Override the output if it exists
//            .addOutput("rtmp://172.16.10.16:1935/hls/1")   // Filename for the destination
            .addOutput(outputBuilder);

        FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
        FFmpegJob job = executor.createJob(builder);
        map.put(1,job);
        try{
            job.run();
        }catch(Exception e){
            //TODO
            map.remove(1);
        }
    }
}

由于实时视频流一直有输入 实际使用需要再某个时间去触发关闭,而FFmpegJob实现的是Runnable接口

将FFmpegExecutor部分改为ProcessBuilder来获取实际运行的线程,同时可以将该线程的引用对象放在map中,方便去操作该线程

private static Map<String, Process> map = new ConcurrentHashMap<>(8);
List<String> args1 = new ArrayList<>();
args1.add(ffmpeg.getPath());
args1.addAll(builder.build());
ProcessBuilder processBuilder = new ProcessBuilder(args1);
processBuilder.redirectErrorStream(true);
Process p = processBuilder.start();
map.put(id, p);

这样可以在需要的时候直接调用p.destroy()来关闭线程

ffmpeg参数较多 而开源组件并未将所有参数都实例化 所以实际使用中可以继承FFmpegOutputBuilder,来添加那些未包涵的参数

(具体可以参考源码中的AbstractFFmpegStreamBuilder的写法)

/**
 * 为了方便拓展api中没有的参数
 */
public class GkFFmpegBuilder extends FFmpegBuilder {

    private Long itsoffset;

    @Override
    public List<String> build() {
        // 该数组为不可变数组 (原因未知)
        List<String> args = super.build();
        List<String> temp = new ArrayList<>();
        if(this.itsoffset != null){
            temp.add("-itsoffset");
            temp.add(this.itsoffset.toString());
        }

        for(String arg: args){
            temp.add(arg);
        }
        List<String> newargs = ImmutableList.copyOf(temp);
        return newargs;
    }

    public Long getItsoffset() {
        return itsoffset;
    }

    public GkFFmpegBuilder setItsoffset(Long itsoffset) {
        this.itsoffset = itsoffset;
        return this;
    }

}
发布了62 篇原创文章 · 获赞 4 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_20282955/article/details/104206230