This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new ba43e7d4ce TIKA-4900: lower defaults (#3197)
ba43e7d4ce is described below

commit ba43e7d4ce468e42d78c077c16568329a822cde5
Author: Tim Allison <[email protected]>
AuthorDate: Fri Sep 18 15:05:42 2026 -0400

    TIKA-4900: lower defaults (#3197)
---
 CHANGES.txt                                        |  3 +++
 .../ROOT/pages/configuration/inference.adoc        | 24 +++++++++++++---------
 .../apache/tika/cli/TikaCLIMediaBindingsTest.java  | 12 +++++------
 .../apache/tika/parser/inference/MediaConfig.java  |  5 +++--
 .../tika/inference/EmbedTaskMediaContractTest.java |  8 ++++----
 .../apache/tika/inference/EmbedTaskMediaTest.java  | 19 ++++++++---------
 .../apache/tika/inference/FfmpegSegmenterTest.java | 16 +++++++++------
 .../tika/config/loader/InferenceLoaderTest.java    |  2 +-
 8 files changed, 50 insertions(+), 39 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 75af8ccca4..3336a59aac 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -25,6 +25,9 @@ Release 4.1.0 - unreleased
      bytes twice. (TIKA-4901).
 
    * Enable audio/video chunking with ffmpeg and inference (TIKA-4900).
+     The default segment grid is 25 s windows with 5 s overlap: hosted omni
+     engines cap an audio item at 30 s inclusive, so the earlier 30 s default
+     had every audio segment refused.
 
    * Refactor OCR and inference to use engines for inference and 
      page rendering (TIKA-4889).
diff --git a/docs/modules/ROOT/pages/configuration/inference.adoc 
b/docs/modules/ROOT/pages/configuration/inference.adoc
index 641003a3f0..d31a72d2f2 100644
--- a/docs/modules/ROOT/pages/configuration/inference.adoc
+++ b/docs/modules/ROOT/pages/configuration/inference.adoc
@@ -339,11 +339,12 @@ in the parse context:
 
 [source,json]
 ----
-{ "parse-context": { "media": { "segment": { "seconds": 30, "overlap": 5 }, 
"maxSegments": 480 } } }
+{ "parse-context": { "media": { "segment": { "seconds": 25, "overlap": 5 }, 
"maxSegments": 480 } } }
 ----
 
-Those are the defaults: 30 s windows, each starting 25 s after the last, so 
any event of
-5 s or less is whole in at least one segment; `maxSegments` caps a document, 
the rest of the
+Those are the defaults: 25 s windows, each starting 20 s after the last, so 
any event of
+5 s or less is whole in at least one segment, and no audio item reaches the 30 
s cap the
+hosted omni engines enforce (see <<jina-omni>>); `maxSegments` caps a 
document, the rest of the
 file is not embedded. A block with `overlap` outside `[0, seconds)`, or 
`maxSegments` of 0
 or below -1, fails at config load and per request. The segmenter defines the 
unit and every binding on the document sees
 the same segments, so each segment's chunks share the segment's id: a chunk 
carries a
@@ -354,10 +355,10 @@ segment's id. Group by `correlator` to get a segment with 
both channels:
 [source,json]
 ----
 [
-  { "producer": "jina-video", "modality": "visual", "correlator": 
"t:25000-55000",
-    "vector": "<base64 float32>", "locators": { "temporal": [{"start_ms": 
25000, "end_ms": 55000}] } },
-  { "producer": "jina-audio", "modality": "audio", "correlator": 
"t:25000-55000",
-    "vector": "<base64 float32>", "locators": { "temporal": [{"start_ms": 
25000, "end_ms": 55000}] } }
+  { "producer": "jina-video", "modality": "visual", "correlator": 
"t:20000-45000",
+    "vector": "<base64 float32>", "locators": { "temporal": [{"start_ms": 
20000, "end_ms": 45000}] } },
+  { "producer": "jina-audio", "modality": "audio", "correlator": 
"t:20000-45000",
+    "vector": "<base64 float32>", "locators": { "temporal": [{"start_ms": 
20000, "end_ms": 45000}] } }
 ]
 ----
 
@@ -385,6 +386,7 @@ the wait counted as progress. When the budget runs out 
mid-file the task writes
 it has embedded, stops, and reports the timeout once as the binding's warning 
on the root
 document; every later MEDIA binding's first probe fails fast the same way.
 
+[[jina-omni]]
 === Jina omni
 
 `jina-embeddings-v5-omni-small` (1024 dimensions) and `-nano` (768) embed 
text, images,
@@ -423,11 +425,13 @@ comparable across the picture, sound and text fields. 
`truncate` and `late_chunk
 to text items. `embedding_type` is refused: the engine reads float vectors by 
index.
 |===
 
-Jina's documented limits: images 5 MB, PDFs 8 MB and one per request; audio as 
WAV, MP3,
-FLAC, OGG, M4A or Opus with no documented duration cap; video as 32 uniformly 
sampled frames
+Jina's limits: images 5 MB, PDFs 8 MB and one per request; audio as WAV, MP3, 
FLAC, OGG,
+M4A or Opus, capped at 30 s per item, inclusive (a 30.0 s clip is refused with 
HTTP 400 and
+the binding's warning names it), which is why the default window is 25 s and a 
`media` block
+for this engine should keep `seconds` below 30; video as 32 uniformly sampled 
frames
 whatever its length, and nothing says the video item's audio track is used, 
which is why
 Tika sends the sound as its own item. The 32 frames are why the grid matters: 
a whole film
-as one input is 32 frames for the film, a 30 s segment is about one frame a 
second. Byte
+as one input is 32 frames for the film, a 25 s segment is more than one frame 
a second. Byte
 caps for audio and video items are not documented; measure before raising 
`maxBytes`.
 
 A query is the same call with the query task, and one vector searches every 
field:
diff --git 
a/tika-app/src/test/java/org/apache/tika/cli/TikaCLIMediaBindingsTest.java 
b/tika-app/src/test/java/org/apache/tika/cli/TikaCLIMediaBindingsTest.java
index 977b58bb2b..40ab186231 100644
--- a/tika-app/src/test/java/org/apache/tika/cli/TikaCLIMediaBindingsTest.java
+++ b/tika-app/src/test/java/org/apache/tika/cli/TikaCLIMediaBindingsTest.java
@@ -66,12 +66,12 @@ public class TikaCLIMediaBindingsTest {
         assumeTrue(answers("ffmpeg") && answers("ffprobe"), "ffmpeg not on the 
PATH");
         Path mp4 = tmp.resolve("clip.mp4");
         assertEquals(0, ProcessUtils.execute(new ProcessBuilder("ffmpeg", 
"-loglevel", "error",
-                "-y", "-f", "lavfi", "-i", 
"testsrc=duration=70:size=160x120:rate=5",
-                "-f", "lavfi", "-i", "sine=frequency=440:duration=70",
+                "-y", "-f", "lavfi", "-i", 
"testsrc=duration=65:size=160x120:rate=5",
+                "-f", "lavfi", "-i", "sine=frequency=440:duration=65",
                 "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", 
"-shortest",
                 mp4.toString()), 60_000, 10_000, 10_000).getExitValue());
         try (TikaTestHttpServer server = new TikaTestHttpServer()) {
-            // 70 s at 30/5 is three cells; one request per binding at the 
default batch size
+            // 65 s at 25/5 is three cells; one request per binding at the 
default batch size
             server.enqueue(new TikaTestHttpServer.MockResponse(200, 
response(3)));
             server.enqueue(new TikaTestHttpServer.MockResponse(200, 
response(3)));
             Path config = tmp.resolve("tika-config.json");
@@ -103,9 +103,9 @@ public class TikaCLIMediaBindingsTest {
             assertEquals(6, chunks.size(), "three cells, two channels");
             Chunk video2 = chunks.get(1);
             Chunk audio2 = chunks.get(4);
-            assertEquals(25000, 
video2.getLocators().getTemporal().get(0).getStartMs());
-            assertEquals(55000, 
video2.getLocators().getTemporal().get(0).getEndMs());
-            assertEquals("t:25000-55000", video2.getCorrelator());
+            assertEquals(20000, 
video2.getLocators().getTemporal().get(0).getStartMs());
+            assertEquals(45000, 
video2.getLocators().getTemporal().get(0).getEndMs());
+            assertEquals("t:20000-45000", video2.getCorrelator());
             assertEquals(video2.getCorrelator(), audio2.getCorrelator());
             assertEquals(Modality.VISUAL, video2.getModality());
             assertEquals(Modality.AUDIO, audio2.getModality());
diff --git 
a/tika-core/src/main/java/org/apache/tika/parser/inference/MediaConfig.java 
b/tika-core/src/main/java/org/apache/tika/parser/inference/MediaConfig.java
index a359ed495a..018c61b964 100644
--- a/tika-core/src/main/java/org/apache/tika/parser/inference/MediaConfig.java
+++ b/tika-core/src/main/java/org/apache/tika/parser/inference/MediaConfig.java
@@ -25,7 +25,8 @@ import org.apache.tika.exception.TikaConfigException;
 /**
  * The {@code "media"} parse-context block: how audio and video are cut into 
segments before
  * a MEDIA binding sees them. One grid per document, shared by every binding, 
so the vectors of
- * one segment land on one chunk. Defaults: 30 s windows, 5 s overlap, 480 
segments.
+ * one segment land on one chunk. Defaults: 25 s windows, 5 s overlap, 480 
segments; 25 s
+ * keeps an audio item under the 30 s cap hosted omni engines enforce (Jina 
rejects 30.0 s).
  */
 @TikaComponent(name = "media", spi = false)
 public class MediaConfig implements Serializable, Initializable {
@@ -34,7 +35,7 @@ public class MediaConfig implements Serializable, 
Initializable {
 
     public static class Segment implements Serializable {
         private static final long serialVersionUID = 1L;
-        private int seconds = 30;
+        private int seconds = 25;
         private int overlap = 5;
 
         public int getSeconds() {
diff --git 
a/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaContractTest.java
 
b/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaContractTest.java
index 2b9ecf8723..2ba1d62d3f 100644
--- 
a/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaContractTest.java
+++ 
b/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaContractTest.java
@@ -51,7 +51,7 @@ import org.apache.tika.parser.inference.Modality;
  */
 public class EmbedTaskMediaContractTest {
 
-    /** Probes every file as 70 s of audio and video; cuts are one-byte files. 
*/
+    /** Probes every file as 65 s of audio and video; cuts are one-byte files. 
*/
     static final class FakeSegmenter implements MediaSegmenter {
         final boolean available;
         final List<Path> cutDirs = new ArrayList<>();
@@ -59,7 +59,7 @@ public class EmbedTaskMediaContractTest {
         Path failProbe;
         int timeoutOnCut = -1;
         int ioOnCut = -1;
-        long durationMs = 70_000;
+        long durationMs = 65_000;
 
         FakeSegmenter(boolean available) {
             this.available = available;
@@ -158,7 +158,7 @@ public class EmbedTaskMediaContractTest {
         }
     }
 
-    /** 70 s at 30/5 is three cells: [0,30] [25,55] [50,70]; batch 2 means 
requests of 2 and 1. */
+    /** 65 s at 25/5 is three cells: [0,25] [20,45] [40,65]; batch 2 means 
requests of 2 and 1. */
     @Test
     public void testHappyPathReleasesTheDir() throws Exception {
         server.enqueue(new TikaTestHttpServer.MockResponse(200, response(2)));
@@ -184,7 +184,7 @@ public class EmbedTaskMediaContractTest {
         List<Chunk> chunks = chunks(unit);
         assertEquals(2, chunks.size(), "the good cells keep their vectors");
         assertEquals(0, 
chunks.get(0).getLocators().getTemporal().get(0).getStartMs());
-        assertEquals(50_000, 
chunks.get(1).getLocators().getTemporal().get(0).getStartMs());
+        assertEquals(40_000, 
chunks.get(1).getLocators().getTemporal().get(0).getStartMs());
         assertEquals(3, fake.cuts, "no cell is cut twice");
         assertEquals(4, server.getRequestCount());
         assertReleased();
diff --git 
a/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaTest.java
 
b/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaTest.java
index f51285419f..6ce8385a4c 100644
--- 
a/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaTest.java
+++ 
b/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/EmbedTaskMediaTest.java
@@ -120,10 +120,9 @@ public class EmbedTaskMediaTest {
     @Test
     public void testAudioAndVideoJoinOnTheCell() throws Exception {
         InferenceUnit unit = unit(clip(70, true), "video/mp4");
-        // 70 s at 30/5: [0,30] [25,55] [50,70]; batch size 2 -> requests of 2 
and 1 per channel
-        for (int i = 0; i < 2; i++) {
+        // 70 s at 25/5: [0,25] [20,45] [40,65] [60,70]; batch size 2 -> two 
requests of 2 per channel
+        for (int i = 0; i < 4; i++) {
             server.enqueue(new TikaTestHttpServer.MockResponse(200, 
response(2)));
-            server.enqueue(new TikaTestHttpServer.MockResponse(200, 
response(1)));
         }
         EmbedTask task = new EmbedTask();
         InferenceBinding video = binding("jina-video", Modality.VISUAL);
@@ -144,19 +143,19 @@ public class EmbedTaskMediaTest {
 
         List<Chunk> chunks = ChunkSerializer.fromJson(
                 unit.getTarget().get(TikaCoreProperties.TIKA_CHUNKS));
-        assertEquals(6, chunks.size(), "one chunk per cell per channel");
+        assertEquals(8, chunks.size(), "one chunk per cell per channel");
         Chunk video2 = chunks.get(1);
-        Chunk audio2 = chunks.get(4);
-        assertEquals(25000, 
video2.getLocators().getTemporal().get(0).getStartMs());
-        assertEquals(55000, 
video2.getLocators().getTemporal().get(0).getEndMs());
-        assertEquals(70000, 
chunks.get(2).getLocators().getTemporal().get(0).getEndMs());
-        assertEquals("t:25000-55000", video2.getCorrelator());
+        Chunk audio2 = chunks.get(5);
+        assertEquals(20000, 
video2.getLocators().getTemporal().get(0).getStartMs());
+        assertEquals(45000, 
video2.getLocators().getTemporal().get(0).getEndMs());
+        assertEquals(70000, 
chunks.get(3).getLocators().getTemporal().get(0).getEndMs());
+        assertEquals("t:20000-45000", video2.getCorrelator());
         assertEquals(video2.getCorrelator(), audio2.getCorrelator(), "the 
cell's id groups them");
         assertEquals("jina-video", video2.getProducer());
         assertEquals(Modality.VISUAL, video2.getModality());
         assertEquals(Modality.AUDIO, audio2.getModality());
         assertEquals(1.0f, video2.getVector()[0], "placed by index");
-        assertEquals(0.0f, chunks.get(5).getVector()[0], "second request, 
index 0");
+        assertEquals(0.0f, chunks.get(6).getVector()[0], "second audio 
request, index 0");
     }
 
     /** The request's media block sets the grid; maxSegments cuts the tail. */
diff --git 
a/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/FfmpegSegmenterTest.java
 
b/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/FfmpegSegmenterTest.java
index a3f5a552a3..4201b20ccd 100644
--- 
a/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/FfmpegSegmenterTest.java
+++ 
b/tika-parsers/tika-parsers-ml/tika-inference/src/test/java/org/apache/tika/inference/FfmpegSegmenterTest.java
@@ -43,11 +43,14 @@ public class FfmpegSegmenterTest {
     @Test
     public void testCells() throws Exception {
         MediaConfig config = new MediaConfig();
-        List<MediaSegmenter.Cell> cells = MediaSegmenter.cells(52_000, config, 
Integer.MAX_VALUE);
+        // defaults 25/5: [0,25] [20,44]; the next start (40) is inside the 
last cell
+        List<MediaSegmenter.Cell> cells = MediaSegmenter.cells(44_000, config, 
Integer.MAX_VALUE);
         assertEquals(2, cells.size(), "a tail inside the previous cell's 
overlap is not a cell");
-        assertEquals(25_000, cells.get(1).startMs());
-        assertEquals(52_000, cells.get(1).endMs());
-        assertEquals(1, MediaSegmenter.cells(30_000, config, 
Integer.MAX_VALUE).size());
+        assertEquals(20_000, cells.get(1).startMs());
+        assertEquals(44_000, cells.get(1).endMs());
+        assertEquals(1, MediaSegmenter.cells(25_000, config, 
Integer.MAX_VALUE).size());
+        assertEquals(2, MediaSegmenter.cells(30_000, config, 
Integer.MAX_VALUE).size(),
+                "a 30 s file is two cells: no cell may reach a 30 s audio 
cap");
         assertEquals(1, MediaSegmenter.cells(1_000, config, 
Integer.MAX_VALUE).size());
         assertEquals(0, MediaSegmenter.cells(0, config, 
Integer.MAX_VALUE).size());
         // a container may claim any duration: the cap bounds the work, not 
the claim
@@ -55,8 +58,9 @@ public class FfmpegSegmenterTest {
         config.getSegment().setOverlap(-5);
         assertThrows(TikaException.class, () -> MediaSegmenter.cells(52_000, 
config, 10),
                 "a negative overlap would leave gaps");
-        config.getSegment().setOverlap(30);
-        assertThrows(TikaException.class, () -> MediaSegmenter.cells(52_000, 
config, 10));
+        config.getSegment().setOverlap(25);
+        assertThrows(TikaException.class, () -> MediaSegmenter.cells(52_000, 
config, 10),
+                "overlap equal to the window is a zero stride");
     }
 
     @Test
diff --git 
a/tika-serialization/src/test/java/org/apache/tika/config/loader/InferenceLoaderTest.java
 
b/tika-serialization/src/test/java/org/apache/tika/config/loader/InferenceLoaderTest.java
index 8d1448db22..233f596a4b 100644
--- 
a/tika-serialization/src/test/java/org/apache/tika/config/loader/InferenceLoaderTest.java
+++ 
b/tika-serialization/src/test/java/org/apache/tika/config/loader/InferenceLoaderTest.java
@@ -288,7 +288,7 @@ public class InferenceLoaderTest {
         assertEquals(10, media.getSegment().getSeconds());
         assertEquals(2, media.getSegment().getOverlap());
         assertEquals(7, media.getMaxSegments());
-        assertEquals(30, new MediaConfig().getSegment().getSeconds(), "default 
window");
+        assertEquals(25, new MediaConfig().getSegment().getSeconds(), "default 
window");
         assertEquals(5, new MediaConfig().getSegment().getOverlap(), "default 
overlap");
         assertThrows(TikaConfigException.class, () -> load("{" + ENGINES + ", 
\"inference\": ["
                 + " { \"id\": \"a\", \"engine\": \"one\", \"input\": 
\"MEDIA\","

Reply via email to