/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.service.metadata;
import org.apache.commons.io.FilenameUtils;
import org.libresonic.player.Logger;
import org.libresonic.player.domain.MediaFile;
import org.libresonic.player.io.InputStreamReaderThread;
import org.libresonic.player.service.ServiceLocator;
import org.libresonic.player.service.TranscodingService;
import org.libresonic.player.util.StringUtil;
import java.io.File;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Parses meta data from video files using FFmpeg (http://ffmpeg.org/).
* <p/>
* Currently duration, bitrate and dimension are supported.
*
* @author Sindre Mehus
*/
public class FFmpegParser extends MetaDataParser {
private static final Logger LOG = Logger.getLogger(FFmpegParser.class);
private static final Pattern DURATION_PATTERN = Pattern.compile("Duration: (\\d+):(\\d+):(\\d+).(\\d+)");
private static final Pattern BITRATE_PATTERN = Pattern.compile("bitrate: (\\d+) kb/s");
private static final Pattern DIMENSION_PATTERN = Pattern.compile("Video.*?, (\\d+)x(\\d+)");
private static final Pattern PAR_PATTERN = Pattern.compile("PAR (\\d+):(\\d+)");
private TranscodingService transcodingService;
/**
* Parses meta data for the given music file. No guessing or reformatting is done.
*
*
* @param file The music file to parse.
* @return Meta data for the file.
*/
@Override
public MetaData getRawMetaData(File file) {
MetaData metaData = new MetaData();
try {
File ffmpeg = new File(transcodingService.getTranscodeDirectory(), "ffmpeg");
String[] command = new String[]{ffmpeg.getAbsolutePath(), "-i", file.getAbsolutePath()};
Process process = Runtime.getRuntime().exec(command);
InputStream stdout = process.getInputStream();
InputStream stderr = process.getErrorStream();
// Consume stdout, we're not interested in that.
new InputStreamReaderThread(stdout, "ffmpeg", true).start();
// Read everything from stderr. It will contain text similar to:
// Input #0, avi, from 'foo.avi':
// Duration: 00:00:33.90, start: 0.000000, bitrate: 2225 kb/s
// Stream #0.0: Video: mpeg4, yuv420p, 352x240 [PAR 1:1 DAR 22:15], 29.97 fps, 29.97 tbr, 29.97 tbn, 30k tbc
// Stream #0.1: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s
String[] lines = StringUtil.readLines(stderr);
Integer width = null;
Integer height = null;
Double par = 1.0;
for (String line : lines) {
Matcher matcher = DURATION_PATTERN.matcher(line);
if (matcher.find()) {
int hours = Integer.parseInt(matcher.group(1));
int minutes = Integer.parseInt(matcher.group(2));
int seconds = Integer.parseInt(matcher.group(3));
metaData.setDurationSeconds(hours * 3600 + minutes * 60 + seconds);
}
matcher = BITRATE_PATTERN.matcher(line);
if (matcher.find()) {
metaData.setBitRate(Integer.valueOf(matcher.group(1)));
}
matcher = DIMENSION_PATTERN.matcher(line);
if (matcher.find()) {
width = Integer.valueOf(matcher.group(1));
height = Integer.valueOf(matcher.group(2));
}
// PAR = Pixel Aspect Rate
matcher = PAR_PATTERN.matcher(line);
if (matcher.find()) {
int a = Integer.parseInt(matcher.group(1));
int b = Integer.parseInt(matcher.group(2));
if (a > 0 && b > 0) {
par = (double) a / (double) b;
}
}
}
if (width != null && height != null) {
width = (int) Math.round(width.doubleValue() * par);
metaData.setWidth(width);
metaData.setHeight(height);
}
} catch (Throwable x) {
LOG.warn("Error when parsing metadata in " + file, x);
}
return metaData;
}
/**
* Not supported.
*/
@Override
public void setMetaData(MediaFile file, MetaData metaData) {
throw new RuntimeException("setMetaData() not supported in " + getClass().getSimpleName());
}
/**
* Returns whether this parser supports tag editing (using the {@link #setMetaData} method).
*
* @return Always false.
*/
@Override
public boolean isEditingSupported() {
return false;
}
/**
* Returns whether this parser is applicable to the given file.
*
* @param file The file in question.
* @return Whether this parser is applicable to the given file.
*/
@Override
public boolean isApplicable(File file) {
String format = FilenameUtils.getExtension(file.getName()).toLowerCase();
for (String s : ServiceLocator.getSettingsService().getVideoFileTypesAsArray()) {
if (format.equals(s)) {
return true;
}
}
return false;
}
public void setTranscodingService(TranscodingService transcodingService) {
this.transcodingService = transcodingService;
}
}