1

Bear with my noobness, I am learning web-flux. I had this simple application that takes a video and extract the audio using FFprobe and FFmpeg, so I thought of redoing it reactively, but I am failing miserably...

Controller:

@PostMapping("/upload")
public String upload(@RequestPart("file") Mono<FilePart> filePartMono, final Model model) {
    Flux<String> filenameList = mediaComponent.extractAudio(filePartMono);
    model.addAttribute("filenameList", new ReactiveDataDriverContextVariable(filenameList));
    return "download";
}

Function to get audio streams out of the video:

public Mono<FFprobeResult> getAudioStreams(InputStream inputStream) {
    try {
        return Mono.just(FFprobe.atPath(FFprobePath)
                .setShowStreams(true)
                .setSelectStreams(StreamType.AUDIO)
                .setLogLevel(LogLevel.INFO)
                .setInput(inputStream)
                .execute());
    } catch (JaffreeException e) {
        log.error(e.getMessage(), e);
        return Mono.error(new MediaException("Audio formats could not be identified."));
    }
}

Attempt 1:

public Flux<String> extractAudio(Mono<FilePart> filePartMono) {
    filePartMono.flatMapMany(Part::content)
            .map(dataBuffer -> dataBuffer.asInputStream(true))
            .flatMap(this::getAudioStreams)
            .subscribe(System.out::println);
    ...
}

Attempt 2:

public Flux<String> extractAudio(Mono<FilePart> filePartMono) {
    filePartMono.flatMapMany(Part::content)
            .reduce(InputStream.nullInputStream(), (inputStream, dataBuffer) -> new SequenceInputStream(
                    inputStream, dataBuffer.asInputStream()
            ))
            .flatMap(this::getAudioStreams)
            .subscribe(System.out::println);
    ...
}

Attempt 3:

public Flux<String> extractAudio(Mono<FilePart> filePartMono) {
    DataBufferUtils.write(filePartMono.flatMapMany(Part::content), OutputStream.nullOutputStream())
            .map(dataBuffer -> dataBuffer.asInputStream(true))
            .flatMap(this::getAudioStreams)
            .subscribe(System.out::println);
    ...
}

Attempt 1 and 3 seems to be the same in the end, FFprobe complains as follows:

2022-10-30 11:24:30.292  WARN 79049 --- [         StdErr] c.g.k.jaffree.process.BaseStdReader      : [mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f9162702340] [warning] STSZ atom truncated
2022-10-30 11:24:30.292 ERROR 79049 --- [         StdErr] c.g.k.jaffree.process.BaseStdReader      : [mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f9162702340] [error] stream 0, contradictionary STSC and STCO
2022-10-30 11:24:30.292 ERROR 79049 --- [         StdErr] c.g.k.jaffree.process.BaseStdReader      : [mov,mp4,m4a,3gp,3g2,mj2 @ 0x7f9162702340] [error] error reading header
2022-10-30 11:24:30.294 ERROR 79049 --- [         StdErr] c.g.k.jaffree.process.BaseStdReader      : [error] tcp://127.0.0.1:51532: Invalid data found when processing input
2022-10-30 11:24:30.295  INFO 79049 --- [oundedElastic-3] c.g.k.jaffree.process.ProcessHandler     : Process has finished with status: 1
2022-10-30 11:24:30.409 ERROR 79049 --- [oundedElastic-3] c.e.s.application.MediaComponent         : Process execution has ended with non-zero status: 1. Check logs for detailed error message.

Attempt 2 produces multiple of these:

Exception in thread "Runnable-0" java.lang.StackOverflowError
    at java.base/java.io.SequenceInputStream.read(SequenceInputStream.java:198)

Now, after some more research, I had another fourth attempt:

public Flux<String> extractAudio(Mono<FilePart> filePartMono) {
    try {
        FFprobeResult FFprobeResult = getAudioStreams(getInputStreamFromFluxDataBuffer(filePartMono.flatMapMany(Part::content))).subscribe(System.out::println);
    return Flux.just("file ", "file2").delayElements(Duration.ofMinutes(1));
    } catch (IOException e) {
        log.error(e.getMessage(), e);
        return Flux.error(new MediaException("Audio extraction failed"));
    }
}

public InputStream getInputStreamFromFluxDataBuffer(Flux<DataBuffer> dataBuffer) throws IOException {
    PipedOutputStream pipedOutputStream = new PipedOutputStream();
    PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
    DataBufferUtils.write(dataBuffer, pipedOutputStream)
        .subscribeOn(Schedulers.boundedElastic())
        .doOnComplete(() -> {
            try {
                pipedOutputStream.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        })
        .subscribe(DataBufferUtils.releaseConsumer());
    return pipedInputStream;
}

However, this time FFprobe starts and never ends, as if the InputStream was endless..

Could anybody point me in the right direction? What am I doing wrong? By the way, I am outputting to console just to see a result, but in the end I need to take all the outputted streams and pass them as arguments to another function that will finally extract the audio, so I need to figure out that as well.

Thank you in advance.

tmarwen
  • 15,750
  • 5
  • 43
  • 62
Einfari
  • 13
  • 3

0 Answers0