From 519752d5d3f1f2ec376bd1d442a1451aff7b068b Mon Sep 17 00:00:00 2001 From: Denis Kokorin Date: Sat, 6 Nov 2021 07:51:46 +0200 Subject: [PATCH] Fix ChannelInput issue. Channel is rewind to first byte every time FTP data connection is established. --- .../github/kokorin/jaffree/net/FtpServer.java | 16 ++++++----- .../github/kokorin/jaffree/util/IOUtil.java | 2 +- .../com/github/kokorin/jaffree/Artifacts.java | 5 ++++ .../kokorin/jaffree/ffmpeg/FFmpegTest.java | 27 +++++++++++++++++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java b/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java index ba1f834d..cdaeae6b 100644 --- a/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java +++ b/src/main/java/com/github/kokorin/jaffree/net/FtpServer.java @@ -268,6 +268,8 @@ private void doPasv(final OutputStream output, final ServerSocket dataServerSock int portLow = port & 0xFF; println(output, "227 Entering Passive Mode (" + address + "," + portHi + "," + portLow + ")."); + //After entering passive mode FTP server should retrieve a file from the first byte. + channel.position(0); } /** @@ -281,13 +283,13 @@ private void doRetr(final OutputStream output, final ServerSocket dataServerSock throws IOException { println(output, "150 File status okay; about to open data connection."); - long copied = 0; try (Socket dataSocket = dataServerSocket.accept(); OutputStream dataOutput = dataSocket.getOutputStream()) { - LOGGER.debug("Data connection established: {}", dataSocket); - copied = IOUtil.copy(Channels.newInputStream(channel), dataOutput, buffer); - dataOutput.flush(); + LOGGER.debug("Data connection established, position: {} socket: {}", channel.position(), + dataSocket); + long copied = IOUtil.copy(Channels.newInputStream(channel), dataOutput, buffer); LOGGER.debug("Copied {} bytes to data socket", copied); + dataOutput.flush(); println(output, "226 Operation successful"); } catch (SocketException e) { // ffmpeg can close data connection without fully reading requested data. @@ -311,11 +313,11 @@ private void doStor(final OutputStream output, final ServerSocket dataServerSock final String path) throws IOException { println(output, "150 File status okay; about to open data connection."); - long copied = 0; try (Socket dataSocket = dataServerSocket.accept(); InputStream dataInput = dataSocket.getInputStream()) { - LOGGER.debug("Data connection established: {}", dataSocket); - copied = IOUtil.copy(dataInput, Channels.newOutputStream(channel), buffer); + LOGGER.debug("Data connection established, position: {} socket: {}", channel.position(), + dataSocket); + long copied = IOUtil.copy(dataInput, Channels.newOutputStream(channel), buffer); LOGGER.debug("Copied {} bytes from data socket", copied); println(output, "226 Operation successful"); } catch (SocketException e) { diff --git a/src/main/java/com/github/kokorin/jaffree/util/IOUtil.java b/src/main/java/com/github/kokorin/jaffree/util/IOUtil.java index 8d5f6e1b..56c28bf1 100644 --- a/src/main/java/com/github/kokorin/jaffree/util/IOUtil.java +++ b/src/main/java/com/github/kokorin/jaffree/util/IOUtil.java @@ -62,7 +62,7 @@ public static long copy(final InputStream input, final OutputStream output, public static long copy(final InputStream input, final OutputStream output, final byte[] buffer) throws IOException { if (buffer.length == 0) { - throw new IllegalArgumentException("Buffer must be not empty"); + throw new IllegalArgumentException("Buffer must be not empty"); } long count = 0; diff --git a/src/test/java/com/github/kokorin/jaffree/Artifacts.java b/src/test/java/com/github/kokorin/jaffree/Artifacts.java index ce2f2602..4def4702 100644 --- a/src/test/java/com/github/kokorin/jaffree/Artifacts.java +++ b/src/test/java/com/github/kokorin/jaffree/Artifacts.java @@ -18,6 +18,7 @@ public class Artifacts { public static final Path VIDEO_MP4 = getMp4Artifact(180); public static final Path VIDEO_MKV = getMkvArtifact(180); public static final Path VIDEO_FLV = getFlvArtifact(180); + public static final Path VIDEO_TS = getTsArtifact(180); public static final Path SMALL_FLV = getFlvArtifact(20); public static final Path SMALL_MP4 = getMp4Artifact(20); public static final Path VIDEO_WITH_PROGRAMS = getTsArtifactWithPrograms(); @@ -36,6 +37,10 @@ private static Path getFlvArtifact(int duration) { return getArtifact("640x480", 30, 44_100, "flv", duration); } + private static Path getTsArtifact(int duration) { + return getArtifact("640x480", 30, 44_100, "ts", duration); + } + private static synchronized Path getNutArtifact(int duration) { Path source = getMp4Artifact(duration); String filename = source.getFileName().toString().replace(".mp4", ".nut"); diff --git a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java index 29f631d2..c362aa54 100644 --- a/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java +++ b/src/test/java/com/github/kokorin/jaffree/ffmpeg/FFmpegTest.java @@ -27,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -714,6 +715,32 @@ public void testChannelInputSeek() throws IOException { assertTrue(Files.size(outputPath) > 1000); } + @Test + public void testChannelWithNonSeekableInput() throws IOException { + Path inputTs = Artifacts.VIDEO_TS; + Path tempDir = Files.createTempDirectory("jaffree"); + Path outputPng = tempDir.resolve("output1.png"); + try ( + SeekableByteChannel inputChannel = Files.newByteChannel(inputTs, + StandardOpenOption.READ) + ) { + FFmpeg.atPath() + .addInput(ChannelInput.fromChannel(inputChannel).setPosition(2000L)) + .setOverwriteOutput(true) + .addOutput( + UrlOutput.toPath(outputPng) + .setFormat("image2") + .setFrameCount(StreamType.VIDEO, 1L) + .addArguments("-q:v", "1") + .disableStream(StreamType.AUDIO) + .disableStream(StreamType.SUBTITLE) + ) + .execute(); + } + + assertTrue(Files.size(outputPng) > 1000); + } + @Test public void testChannelOutput() throws IOException { Path tempDir = Files.createTempDirectory("jaffree");