use of net.pms.io.ProcessWrapperImpl in project UniversalMediaServer by UniversalMediaServer.
the class DLNAMediaInfo method parse.
/**
* Parse media without using MediaInfo.
*/
public void parse(InputFile inputFile, Format ext, int type, boolean thumbOnly, boolean resume, RendererConfiguration renderer) {
int i = 0;
while (isParsing()) {
if (i == 5) {
mediaparsed = true;
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
i++;
}
if (isMediaparsed() && !thumbOnly) {
// file could be already parsed by MediaInfo and we need only thumbnail
return;
}
if (inputFile != null) {
File file = inputFile.getFile();
if (file != null) {
size = file.length();
} else {
size = inputFile.getSize();
}
ProcessWrapperImpl pw = null;
boolean ffmpeg_parsing = true;
if (type == Format.AUDIO || ext instanceof AudioAsVideo) {
ffmpeg_parsing = false;
DLNAMediaAudio audio = new DLNAMediaAudio();
if (file != null) {
try {
AudioFile af;
if ("mp2".equals(FileUtil.getExtension(file).toLowerCase(Locale.ROOT))) {
af = AudioFileIO.readAs(file, "mp3");
} else {
af = AudioFileIO.read(file);
}
AudioHeader ah = af.getAudioHeader();
if (ah != null && !thumbOnly) {
int length = ah.getTrackLength();
int rate = ah.getSampleRateAsNumber();
if (ah.getEncodingType() != null && ah.getEncodingType().toLowerCase().contains("flac 24")) {
audio.setBitsperSample(24);
}
audio.setSampleFrequency("" + rate);
durationSec = (double) length;
bitrate = (int) ah.getBitRateAsNumber();
// set default value of channels to 2
audio.getAudioProperties().setNumberOfChannels(2);
String channels = ah.getChannels().toLowerCase(Locale.ROOT);
if (StringUtils.isNotBlank(channels)) {
if (channels.equals("1") || channels.contains("mono")) {
// parse value "1" or "Mono"
audio.getAudioProperties().setNumberOfChannels(1);
} else if (!(channels.equals("2") || channels.equals("0") || channels.contains("stereo"))) {
// No need to parse stereo as it's set as default
try {
audio.getAudioProperties().setNumberOfChannels(Integer.parseInt(channels));
} catch (IllegalArgumentException e) {
LOGGER.debug("Could not parse number of audio channels from \"{}\"", channels);
}
}
}
if (StringUtils.isNotBlank(ah.getEncodingType())) {
audio.setCodecA(ah.getEncodingType());
}
if (audio.getCodecA() != null && audio.getCodecA().contains("(windows media")) {
audio.setCodecA(audio.getCodecA().substring(0, audio.getCodecA().indexOf("(windows media")).trim());
}
}
Tag t = af.getTag();
if (t != null) {
if (t.getArtworkList().size() > 0) {
thumb = DLNAThumbnail.toThumbnail(t.getArtworkList().get(0).getBinaryData(), 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false);
} else if (!configuration.getAudioThumbnailMethod().equals(CoverSupplier.NONE)) {
thumb = DLNAThumbnail.toThumbnail(CoverUtil.get().getThumbnail(t), 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false);
}
if (thumb != null) {
thumbready = true;
}
if (!thumbOnly) {
audio.setAlbum(t.getFirst(FieldKey.ALBUM));
audio.setArtist(t.getFirst(FieldKey.ARTIST));
audio.setSongname(t.getFirst(FieldKey.TITLE));
String y = t.getFirst(FieldKey.YEAR);
try {
if (y.length() > 4) {
y = y.substring(0, 4);
}
audio.setYear(Integer.parseInt(((y != null && y.length() > 0) ? y : "0")));
y = t.getFirst(FieldKey.TRACK);
audio.setTrack(Integer.parseInt(((y != null && y.length() > 0) ? y : "1")));
audio.setGenre(t.getFirst(FieldKey.GENRE));
} catch (NumberFormatException | KeyNotFoundException e) {
LOGGER.debug("Error parsing unimportant metadata: " + e.getMessage());
}
}
}
} catch (CannotReadException e) {
if (e.getMessage().startsWith(ErrorMessage.NO_READER_FOR_THIS_FORMAT.getMsg().substring(0, ErrorMessage.NO_READER_FOR_THIS_FORMAT.getMsg().indexOf("{")))) {
LOGGER.debug("No audio tag support for audio file \"{}\"", file.getName());
} else {
LOGGER.error("Error reading audio tag for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
}
} catch (IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException | NumberFormatException | KeyNotFoundException e) {
LOGGER.debug("Error parsing audio file tag for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
ffmpeg_parsing = false;
}
// Set container for formats that the normal parsing fails to do from Format
if (StringUtils.isBlank(container) && ext != null) {
if (ext.getIdentifier() == Identifier.ADPCM) {
audio.setCodecA(FormatConfiguration.ADPCM);
} else if (ext.getIdentifier() == Identifier.DSD) {
audio.setCodecA(FormatConfiguration.DSD);
}
}
if (StringUtils.isNotBlank(audio.getSongname())) {
if (renderer != null && renderer.isPrependTrackNumbers() && audio.getTrack() > 0) {
audio.setSongname(audio.getTrack() + ": " + audio.getSongname());
}
} else {
audio.setSongname(file.getName());
}
if (!ffmpeg_parsing) {
audioTracks.add(audio);
}
}
if (StringUtils.isBlank(container)) {
container = audio.getCodecA();
}
}
if (type == Format.IMAGE && file != null) {
if (!thumbOnly) {
try {
ffmpeg_parsing = false;
ImagesUtil.parseImage(file, this);
imageCount++;
} catch (IOException e) {
LOGGER.debug("Error parsing image \"{}\", switching to FFmpeg: {}", file.getAbsolutePath(), e.getMessage());
LOGGER.trace("", e);
ffmpeg_parsing = true;
}
}
if (thumbOnly && configuration.isThumbnailGenerationEnabled() && configuration.getImageThumbnailsEnabled()) {
LOGGER.trace("Creating thumbnail for \"{}\"", file.getName());
// Create the thumbnail image
try {
if (imageInfo instanceof ExifInfo && ((ExifInfo) imageInfo).hasExifThumbnail() && !imageInfo.isImageIOSupported()) {
/*
* XXX Extraction of thumbnails was removed in version
* 2.10.0 of metadata-extractor because of a bug in
* related code. This section is deactivated while
* waiting for this to be made available again.
*
* Images supported by ImageIO or DCRaw aren't affected,
* so this only applied to very few images anyway.
* It could extract thumbnails for some "raw" images
* if DCRaw was disabled.
*
// ImageIO can't read the file, try to get the embedded Exif thumbnail if it's there.
Metadata metadata;
try {
metadata = ImagesUtil.getMetadata(new FileInputStream(file), imageInfo.getFormat());
} catch (ImageProcessingException e) {
metadata = null;
LOGGER.debug("Unexpected error reading metadata for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
}
thumb = DLNAThumbnail.toThumbnail(
ImagesUtil.getThumbnailFromMetadata(file, metadata),
320,
320,
ScaleType.MAX,
ImageFormat.SOURCE,
false
);
if (thumb == null && LOGGER.isTraceEnabled()) {
LOGGER.trace("Exif thumbnail extraction failed, no thumbnail will be generated for \"{}\"", file.getName());
}*/
} else {
// This will fail with UnknownFormatException for any image formats not supported by ImageIO
thumb = DLNAThumbnail.toThumbnail(Files.newInputStream(file.toPath()), 320, 320, ScaleType.MAX, ImageFormat.SOURCE, false);
}
thumbready = true;
} catch (EOFException e) {
LOGGER.debug("Error generating thumbnail for \"{}\": Unexpected end of file, probably corrupt file or read error.", file.getName());
} catch (UnknownFormatException e) {
LOGGER.debug("Could not generate thumbnail for \"{}\" because the format is unknown: {}", file.getName(), e.getMessage());
} catch (IOException e) {
LOGGER.debug("Error generating thumbnail for \"{}\": {}", file.getName(), e.getMessage());
LOGGER.trace("", e);
}
}
}
if (ffmpeg_parsing) {
if (!thumbOnly || (type == Format.VIDEO && !configuration.isUseMplayerForVideoThumbs())) {
pw = getFFmpegThumbnail(inputFile, resume, renderer);
}
boolean dvrms = false;
String input = "-";
if (file != null) {
input = ProcessUtil.getShortFileNameIfWideChars(file.getAbsolutePath());
dvrms = file.getAbsolutePath().toLowerCase().endsWith("dvr-ms");
}
synchronized (ffmpeg_failureLock) {
if (pw != null && !ffmpeg_failure && !thumbOnly) {
parseFFmpegInfo(pw.getResults(), input);
}
}
if (!thumbOnly && container != null && file != null && container.equals("mpegts") && isH264() && getDurationInSeconds() == 0) {
// Parse the duration
try {
int length = MpegUtil.getDurationFromMpeg(file);
if (length > 0) {
durationSec = (double) length;
}
} catch (IOException e) {
LOGGER.trace("Error retrieving length: " + e.getMessage());
}
}
if (configuration.isUseMplayerForVideoThumbs() && type == Format.VIDEO && !dvrms) {
try {
getMplayerThumbnail(inputFile, resume, renderer);
String frameName = "" + inputFile.hashCode();
frameName = configuration.getTempFolder() + "/mplayer_thumbs/" + frameName + "00000001/00000001.jpg";
frameName = frameName.replace(',', '_');
File jpg = new File(frameName);
if (jpg.exists()) {
try (InputStream is = new FileInputStream(jpg)) {
int sz = is.available();
if (sz > 0) {
byte[] bytes = new byte[sz];
is.read(bytes);
thumb = DLNAThumbnail.toThumbnail(bytes, 640, 480, ScaleType.MAX, ImageFormat.SOURCE, false);
thumbready = true;
}
}
if (!jpg.delete()) {
jpg.deleteOnExit();
}
// Try and retry
if (!jpg.getParentFile().delete() && !jpg.getParentFile().delete()) {
LOGGER.debug("Failed to delete \"" + jpg.getParentFile().getAbsolutePath() + "\"");
}
}
} catch (IOException e) {
LOGGER.debug("Caught exception", e);
}
}
if (type == Format.VIDEO && pw != null && thumb == null) {
byte[] bytes = pw.getOutputByteArray().toByteArray();
if (bytes != null && bytes.length > 0) {
try {
thumb = DLNAThumbnail.toThumbnail(bytes);
} catch (IOException e) {
LOGGER.debug("Error while decoding thumbnail: " + e.getMessage());
LOGGER.trace("", e);
}
thumbready = true;
}
}
}
postParse(type, inputFile);
mediaparsed = true;
}
}
use of net.pms.io.ProcessWrapperImpl in project UniversalMediaServer by UniversalMediaServer.
the class DLNAMediaInfo method getFFmpegThumbnail.
private ProcessWrapperImpl getFFmpegThumbnail(InputFile media, boolean resume, RendererConfiguration renderer) {
/**
* Note: The text output from FFmpeg is used by renderers that do
* not use MediaInfo, so do not make any changes that remove or
* minimize the amount of text given by FFmpeg here
*/
String[] args = new String[14];
args[0] = getFfmpegPath();
File file = media.getFile();
boolean dvrms = file != null && file.getAbsolutePath().toLowerCase().endsWith("dvr-ms");
if (dvrms && isNotBlank(configuration.getFfmpegAlternativePath())) {
args[0] = configuration.getFfmpegAlternativePath();
}
args[1] = "-ss";
if (resume) {
args[2] = Integer.toString((int) getDurationInSeconds());
} else {
args[2] = Integer.toString((int) Math.min(configuration.getThumbnailSeekPos(), getDurationInSeconds()));
}
args[3] = "-i";
if (file != null) {
args[4] = ProcessUtil.getShortFileNameIfWideChars(file.getAbsolutePath());
} else {
args[4] = "-";
}
args[5] = "-an";
args[6] = "-an";
// Thumbnail resolution
int thumbnailWidth = 320;
int thumbnailHeight = 180;
double thumbnailRatio = 1.78;
boolean isThumbnailPadding = true;
if (renderer != null) {
thumbnailWidth = renderer.getThumbnailWidth();
thumbnailHeight = renderer.getThumbnailHeight();
thumbnailRatio = renderer.getThumbnailRatio();
isThumbnailPadding = renderer.isThumbnailPadding();
}
if (isThumbnailPadding) {
args[7] = "-vf";
args[8] = "scale='if(gt(a," + thumbnailRatio + ")," + thumbnailWidth + ",-1)':'if(gt(a," + thumbnailRatio + "),-1," + thumbnailHeight + ")', pad=" + thumbnailWidth + ":" + thumbnailHeight + ":(" + thumbnailWidth + "-iw)/2:(" + thumbnailHeight + "-ih)/2";
} else {
args[7] = "-vf";
args[8] = "scale='if(gt(a," + thumbnailRatio + ")," + thumbnailWidth + ",-1)':'if(gt(a," + thumbnailRatio + "),-1," + thumbnailHeight + ")'";
}
args[9] = "-vframes";
args[10] = "1";
args[11] = "-f";
args[12] = "image2";
args[13] = "pipe:";
// FIXME MPlayer should not be used if thumbnail generation is disabled
if (!configuration.isThumbnailGenerationEnabled() || renderer != null && !renderer.isThumbnails() || configuration.isUseMplayerForVideoThumbs() && !dvrms) {
args[2] = "0";
for (int i = 5; i <= 13; i++) {
args[i] = "-an";
}
}
OutputParams params = new OutputParams(configuration);
params.maxBufferSize = 1;
params.stdin = media.getPush();
// not serious if anything happens during the thumbnailer
params.noexitcheck = true;
// true: consume stderr on behalf of the caller i.e. parse()
final ProcessWrapperImpl pw = new ProcessWrapperImpl(args, true, params, false, true);
// FAILSAFE
synchronized (parsingLock) {
parsing = true;
}
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000);
synchronized (ffmpeg_failureLock) {
ffmpeg_failure = true;
}
} catch (InterruptedException e) {
}
pw.stopProcess();
synchronized (parsingLock) {
parsing = false;
}
}
};
Thread failsafe = new Thread(r, "FFmpeg Thumbnail Failsafe");
failsafe.start();
pw.runInSameThread();
synchronized (parsingLock) {
parsing = false;
}
return pw;
}
use of net.pms.io.ProcessWrapperImpl in project UniversalMediaServer by UniversalMediaServer.
the class DLNAMediaInfo method getAnnexBFrameHeader.
public byte[][] getAnnexBFrameHeader(InputFile f) {
String[] cmdArray = new String[14];
cmdArray[0] = configuration.getFfmpegPath();
cmdArray[1] = "-i";
if (f.getPush() == null && f.getFilename() != null) {
cmdArray[2] = f.getFilename();
} else {
cmdArray[2] = "-";
}
cmdArray[3] = "-vframes";
cmdArray[4] = "1";
cmdArray[5] = "-c:v";
cmdArray[6] = "copy";
cmdArray[7] = "-f";
cmdArray[8] = "h264";
cmdArray[9] = "-bsf";
cmdArray[10] = "h264_mp4toannexb";
cmdArray[11] = "-an";
cmdArray[12] = "-y";
cmdArray[13] = "pipe:";
byte[][] returnData = new byte[2][];
OutputParams params = new OutputParams(configuration);
params.maxBufferSize = 1;
params.stdin = f.getPush();
final ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, true, params);
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
synchronized (ffmpeg_annexb_failureLock) {
ffmpeg_annexb_failure = true;
}
} catch (InterruptedException e) {
}
pw.stopProcess();
}
};
Thread failsafe = new Thread(r, "FFMpeg AnnexB Frame Header Failsafe");
failsafe.start();
pw.runInSameThread();
synchronized (ffmpeg_annexb_failureLock) {
if (ffmpeg_annexb_failure) {
return null;
}
}
byte[] data = pw.getOutputByteArray().toByteArray();
returnData[0] = data;
int kf = 0;
for (int i = 3; i < data.length; i++) {
if (data[i - 3] == 1 && (data[i - 2] & 37) == 37 && (data[i - 1] & -120) == -120) {
kf = i - 2;
break;
}
}
int st = 0;
boolean found = false;
if (kf > 0) {
for (int i = kf; i >= 5; i--) {
if (data[i - 5] == 0 && data[i - 4] == 0 && data[i - 3] == 0 && (data[i - 2] & 1) == 1 && (data[i - 1] & 39) == 39) {
st = i - 5;
found = true;
break;
}
}
}
if (found) {
byte[] header = new byte[kf - st];
System.arraycopy(data, st, header, 0, kf - st);
returnData[1] = header;
}
return returnData;
}
use of net.pms.io.ProcessWrapperImpl in project UniversalMediaServer by UniversalMediaServer.
the class DCRaw method getImage.
/**
* Converts {@code fileName} into PPM format.
*
* @param params the {@link OutputParams} to use. Can be {@code null}.
* @param fileName the path of the image file to process.
* @param imageInfo the {@link ImageInfo} for the image file. Can be {@code null}.
* @return A byte array containing the converted image or {@code null}.
* @throws IOException if an IO error occurs.
*/
@Override
public byte[] getImage(OutputParams params, String fileName, ImageInfo imageInfo) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Decoding image \"{}\" with DCRaw", fileName);
}
if (params == null) {
params = new OutputParams(PMS.getConfiguration());
}
// Use device-specific pms conf
PmsConfiguration configuration = PMS.getConfiguration(params);
params.log = false;
// Setting the buffer to the size of the source file or 5 MB. The
// output won't be the same size as the input, but it will hopefully
// give us a somewhat relevant buffer size. Every time the buffer has
// to grow, the whole buffer must be copied in memory.
params.outputByteArrayStreamBufferSize = imageInfo != null && imageInfo.getSize() != ImageInfo.SIZE_UNKNOWN ? (int) imageInfo.getSize() : 5000000;
// First try to get the embedded thumbnail
String[] cmdArray = new String[5];
cmdArray[0] = configuration.getDCRawPath();
cmdArray[1] = "-c";
cmdArray[2] = "-M";
cmdArray[3] = "-w";
cmdArray[4] = fileName;
ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, true, params, false, true);
pw.runInSameThread();
byte[] bytes = pw.getOutputByteArray().toByteArray();
List<String> results = pw.getResults();
if (bytes == null || bytes.length == 0) {
if (!results.isEmpty() && results.get(0).startsWith("Cannot decode file")) {
LOGGER.warn("DCRaw could not decode image \"{}\"", fileName);
} else if (!results.isEmpty()) {
LOGGER.debug("DCRaw failed to decode image \"{}\": {}", fileName, StringUtils.join(results, "\n"));
}
return null;
}
return bytes;
}
use of net.pms.io.ProcessWrapperImpl in project UniversalMediaServer by UniversalMediaServer.
the class MEncoderWebVideo method launchTranscode.
@Override
public ProcessWrapper launchTranscode(DLNAResource dlna, DLNAMediaInfo media, OutputParams params) throws IOException {
// Use device-specific pms conf
PmsConfiguration prev = configuration;
configuration = (DeviceConfiguration) params.mediaRenderer;
params.minBufferSize = params.minFileSize;
params.secondread_minsize = 100000;
PipeProcess pipe = new PipeProcess("mencoder" + System.currentTimeMillis());
params.input_pipes[0] = pipe;
String[] cmdArray = new String[args().length + 4];
cmdArray[0] = executable();
final String filename = dlna.getFileName();
cmdArray[1] = filename;
System.arraycopy(args(), 0, cmdArray, 2, args().length);
cmdArray[cmdArray.length - 2] = "-o";
cmdArray[cmdArray.length - 1] = pipe.getInputPipe();
ProcessWrapper mkfifo_process = pipe.getPipeProcess();
cmdArray = finalizeTranscoderArgs(filename, dlna, media, params, cmdArray);
ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, params);
pw.attachProcess(mkfifo_process);
/**
* It can take a long time for Windows to create a named pipe (and
* mkfifo can be slow if /tmp isn't memory-mapped), so run this in
* the current thread.
*/
mkfifo_process.runInSameThread();
pipe.deleteLater();
pw.runInNewThread();
// Not sure what good this 50ms wait will do for the calling method.
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
configuration = prev;
return pw;
}
Aggregations