JAVA - Xuggler - объединить аудиофайл MP3 и фильм MP4

Использование JAVA и Xuggler — следующий код объединяет аудиофайл MP3 и файл фильма MP4 и выводит объединенный файл mp4.

Я хочу, чтобы продолжительность выходного видео была равна продолжительности входного видеофайла.

Если я установлю условие цикла:

while (containerVideo.readNextPacket(packetvideo) >= 0) 

Это дает 400 итераций, то есть фильм длится 15 секунд. именно так, как я хочу.

Но по какой-то причине звук обрывается через 10 секунд, что делает последние 5 секунд фильма немыми.

Похоже, что продолжительность одной итерации IStreamCoder видео отличается от продолжительности одной итерации IStreamCoder аудио.

Как сделать так, чтобы звук заполнил все 15 секунд фильма?

    String inputVideoFilePath = "in.mp4";
    String inputAudioFilePath = "in.mp3";
    String outputVideoFilePath = "out.mp4";

    IMediaWriter mWriter = ToolFactory.makeWriter(outputVideoFilePath);

    IContainer containerVideo = IContainer.make();
    IContainer containerAudio = IContainer.make();

    // check files are readable
    if (containerVideo.open(inputVideoFilePath, IContainer.Type.READ, null) < 0)
        throw new IllegalArgumentException("Cant find " + inputVideoFilePath);
    if (containerAudio.open(inputAudioFilePath, IContainer.Type.READ, null) < 0)
        throw new IllegalArgumentException("Cant find " + inputAudioFilePath);

    // read video file and create stream
    IStreamCoder coderVideo = containerVideo.getStream(0).getStreamCoder();
    if (coderVideo.open(null, null) < 0)
        throw new RuntimeException("Cant open video coder");
    IPacket packetvideo = IPacket.make();
    int width = coderVideo.getWidth();
    int height = coderVideo.getHeight();

    // read audio file and create stream
    IStreamCoder coderAudio = containerAudio.getStream(0).getStreamCoder();
    if (coderAudio.open(null, null) < 0)
        throw new RuntimeException("Cant open audio coder");
    IPacket packetaudio = IPacket.make();

    mWriter.addAudioStream(1, 0, coderAudio.getChannels(), coderAudio.getSampleRate());
    mWriter.addVideoStream(0, 0, width, height);

    while (containerVideo.readNextPacket(packetvideo) >= 0) {

        containerAudio.readNextPacket(packetaudio);

        // video packet
        IVideoPicture picture = IVideoPicture.make(coderVideo.getPixelType(), width, height);
        coderVideo.decodeVideo(picture, packetvideo, 0);
        if (picture.isComplete()) 
            mWriter.encodeVideo(0, picture);

        // audio packet 
        IAudioSamples samples = IAudioSamples.make(512, coderAudio.getChannels(), IAudioSamples.Format.FMT_S32);
        coderAudio.decodeAudio(samples, packetaudio, 0);
        if (samples.isComplete()) 
            mWriter.encodeAudio(1, samples);

    }

    coderAudio.close();
    coderVideo.close();
    containerAudio.close();
    containerVideo.close();
    mWriter.close();

person gilad s    schedule 06.11.2013    source источник


Ответы (2)


Хороший код. Спасибо. Чтобы захватить оставшееся аудио, добавьте в конце цикл для чтения аудиопакетов, пока их не останется.

    while (samples.isComplete() ) {
        containerAudio.readNextPacket(packetaudio);
        coderAudio.decodeAudio(samples, packetaudio, 0);
        writer.encodeAudio(iAudioStream, samples);
        Utility.console(String.format("%s %d", Utility.timeStamp(),
            samples.getPts() ));            
    }
person Eli Sokal    schedule 25.11.2013

Я понимаю, что это старо, но я наткнулся на него, и он сделал именно то, что мне было нужно, но у меня была та же проблема (мой звук преждевременно остановился). Ответ Эли сработал, но потребовал некоторой настройки. Я публикую изменения здесь в надежде сэкономить кому-то время в будущем. Отказ от ответственности: я плохо знаю Xuggle и не знаю, лучший ли это способ сделать это, но я знаю, что он сделал свою работу за меня:

    String inputVideoFilePath = "in.mp4";
    String inputAudioFilePath = "in.mp3";
    String outputVideoFilePath = "out.mp4";

    IMediaWriter mWriter = ToolFactory.makeWriter(outputVideoFilePath);

    IContainer containerVideo = IContainer.make();
    IContainer containerAudio = IContainer.make();

    // check files are readable
    if (containerVideo.open(inputVideoFilePath, IContainer.Type.READ, null) < 0)
        throw new IllegalArgumentException("Cant find " + inputVideoFilePath);
    if (containerAudio.open(inputAudioFilePath, IContainer.Type.READ, null) < 0)
        throw new IllegalArgumentException("Cant find " + inputAudioFilePath);

    // read video file and create stream
    IStreamCoder coderVideo = containerVideo.getStream(0).getStreamCoder();
    if (coderVideo.open(null, null) < 0)
        throw new RuntimeException("Cant open video coder");
    IPacket packetvideo = IPacket.make();
    int width = coderVideo.getWidth();
    int height = coderVideo.getHeight();

    // read audio file and create stream
    IStreamCoder coderAudio = containerAudio.getStream(0).getStreamCoder();
    if (coderAudio.open(null, null) < 0)
        throw new RuntimeException("Cant open audio coder");
    IPacket packetaudio = IPacket.make();

    mWriter.addAudioStream(1, 0, coderAudio.getChannels(), coderAudio.getSampleRate());
    mWriter.addVideoStream(0, 0, width, height);

    while (containerVideo.readNextPacket(packetvideo) >= 0) {

        containerAudio.readNextPacket(packetaudio);

        // video packet
        IVideoPicture picture = IVideoPicture.make(coderVideo.getPixelType(), width, height);
        coderVideo.decodeVideo(picture, packetvideo, 0);
        if (picture.isComplete()) 
            mWriter.encodeVideo(0, picture);

        // audio packet 
        IAudioSamples samples = IAudioSamples.make(512, coderAudio.getChannels(), IAudioSamples.Format.FMT_S32);
        coderAudio.decodeAudio(samples, packetaudio, 0);
        if (samples.isComplete()) 
            mWriter.encodeAudio(1, samples);

    }


//<added_code> This is Eli Sokal's code tweaked to work with gilad s' code

    IAudioSamples samples;
    do {
        samples = IAudioSamples.make(512, coderAudio.getChannels(), IAudioSamples.Format.FMT_S32);
        containerAudio.readNextPacket(packetaudio);
        coderAudio.decodeAudio(samples, packetaudio, 0);
        mWriter.encodeAudio(1, samples);
    }while (samples.isComplete() );

//</added_code>


    coderAudio.close();
    coderVideo.close();
    containerAudio.close();
    containerVideo.close();
    mWriter.close();
person Lunchbox    schedule 08.01.2016