Skip to content

Commit c3655a9

Browse files
authored
TIKA-4800: Expose the MP4 frame rate, audio channels and sample size (#2982)
* TIKA-4800: Add video:frame-rate from the MP4 video track * TIKA-4800: Add audio:channels from the MP4 sound track * TIKA-4800: Add audio:bits-per-sample from the MP4 sound track
1 parent dcb7d54 commit c3655a9

5 files changed

Lines changed: 67 additions & 1 deletion

File tree

tika-core/src/main/java/org/apache/tika/metadata/Audio.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,16 @@ public interface Audio {
7171
* protected audio track sets it. Only set when protection is detected.
7272
*/
7373
Property HAS_DRM = Property.internalBoolean("audio:has-drm");
74+
75+
/**
76+
* Number of audio channels (e.g. 2 for stereo). {@link XMPDM#AUDIO_CHANNEL_TYPE}
77+
* only distinguishes Mono from Stereo and cannot represent more than two
78+
* channels. A per-stream value, see {@link #BITRATE}.
79+
*/
80+
Property CHANNELS = Property.internalInteger("audio:channels");
81+
82+
/**
83+
* Audio sample size in bits (e.g. 16). A per-stream value, see {@link #BITRATE}.
84+
*/
85+
Property BITS_PER_SAMPLE = Property.internalInteger("audio:bits-per-sample");
7486
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.tika.metadata;
18+
19+
/**
20+
* Video metadata properties that have no suitable XMPDM equivalent.
21+
* See TIKA-4800.
22+
*
23+
* @since Apache Tika 4.0.0
24+
*/
25+
public interface Video {
26+
27+
/**
28+
* Frame rate in frames per second. {@link XMPDM#VIDEO_FRAME_RATE} is a
29+
* closed-choice text field (24, NTSC, PAL) and cannot carry an arbitrary
30+
* measured rate.
31+
*/
32+
Property FRAME_RATE = Property.internalReal("video:frame-rate");
33+
}

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/main/java/org/apache/tika/parser/mp4/MP4Parser.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@
4545
import org.apache.tika.exception.RuntimeSAXException;
4646
import org.apache.tika.exception.TikaException;
4747
import org.apache.tika.io.TikaInputStream;
48+
import org.apache.tika.metadata.Audio;
4849
import org.apache.tika.metadata.Metadata;
4950
import org.apache.tika.metadata.Property;
5051
import org.apache.tika.metadata.TikaCoreProperties;
52+
import org.apache.tika.metadata.Video;
5153
import org.apache.tika.metadata.XMPDM;
5254
import org.apache.tika.mime.MediaType;
5355
import org.apache.tika.parser.ParseContext;
@@ -158,6 +160,12 @@ private void processMp4VideoDirectory(Mp4VideoDirectory mp4Directory, Metadata m
158160
String compressor = mp4Directory.getString(Mp4VideoDirectory.TAG_COMPRESSOR_NAME);
159161
metadata.set(XMPDM.VIDEO_COMPRESSOR, compressor);
160162
}
163+
Float frameRate = mp4Directory.getFloatObject(Mp4VideoDirectory.TAG_FRAME_RATE);
164+
if (frameRate != null) {
165+
// set as the float's own string: set(Property, double) would widen it
166+
// and print the double-rounding artefact (e.g. 29.969999...).
167+
metadata.set(Video.FRAME_RATE, frameRate.toString());
168+
}
161169
}
162170

163171
/**
@@ -188,9 +196,12 @@ private void processMp4SoundDirectory(Mp4SoundDirectory mp4SoundDirectory,
188196
Metadata metadata) {
189197
addInt(mp4SoundDirectory, metadata, Mp4SoundDirectory.TAG_AUDIO_SAMPLE_RATE,
190198
XMPDM.AUDIO_SAMPLE_RATE);
199+
addInt(mp4SoundDirectory, metadata, Mp4SoundDirectory.TAG_AUDIO_SAMPLE_SIZE,
200+
Audio.BITS_PER_SAMPLE);
191201

192202
try {
193203
int numChannels = mp4SoundDirectory.getInt(Mp4SoundDirectory.TAG_NUMBER_OF_CHANNELS);
204+
metadata.set(Audio.CHANNELS, numChannels);
194205

195206
if (numChannels == 1) {
196207
metadata.set(XMPDM.AUDIO_CHANNEL_TYPE, "Mono");

tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-audiovideo-module/src/test/java/org/apache/tika/parser/mp4/MP4ParserTest.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.apache.tika.metadata.Metadata;
4747
import org.apache.tika.metadata.QuickTime;
4848
import org.apache.tika.metadata.TikaCoreProperties;
49+
import org.apache.tika.metadata.Video;
4950
import org.apache.tika.metadata.XMP;
5051
import org.apache.tika.metadata.XMPDM;
5152
import org.apache.tika.parser.ParseContext;
@@ -121,6 +122,8 @@ public void testMP4ParsingAudio() throws Exception {
121122

122123
assertEquals("44100", metadata.get(XMPDM.AUDIO_SAMPLE_RATE));
123124
assertEquals("Stereo", metadata.get(XMPDM.AUDIO_CHANNEL_TYPE));
125+
assertEquals("2", metadata.get(Audio.CHANNELS));
126+
assertEquals("16", metadata.get(Audio.BITS_PER_SAMPLE));
124127
assertEquals("M4A", metadata.get(XMPDM.AUDIO_COMPRESSOR));
125128
assertEquals("0.07", metadata.get(XMPDM.DURATION));
126129

@@ -179,8 +182,15 @@ public void testMP4MultipleCovers() throws Exception {
179182
back.get(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE));
180183
}
181184

182-
// TODO Test a MP4 Video file
183185
// TODO Test an old QuickTime Video File
186+
@Test
187+
public void testVideoFrameRate() throws Exception {
188+
// a 10 fps H.264 clip generated with ffmpeg (color source, 16x16, 1s)
189+
XMLResult r = getXML("testMP4Video.mp4");
190+
assertEquals("video/mp4", r.metadata.get(Metadata.CONTENT_TYPE));
191+
assertEquals("10.0", r.metadata.get(Video.FRAME_RATE));
192+
}
193+
184194
@Test
185195
@Timeout(30000)
186196
public void testInfiniteLoop() throws Exception {

0 commit comments

Comments
 (0)