/*
* PS3 Media Server, for streaming any medias to your PS3.
* Copyright (C) 2008 A.Brochard
*
* This program 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; version 2
* of the License only.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.pms.encoders;
import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import net.pms.Messages;
import net.pms.configuration.PmsConfiguration;
import net.pms.configuration.RendererConfiguration;
import net.pms.dlna.*;
import net.pms.formats.Format;
import net.pms.io.*;
import net.pms.util.CodecUtil;
import net.pms.util.FormLayoutUtil;
import net.pms.util.PlayerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.*;
import java.net.URL;
import java.util.Locale;
import static net.pms.formats.v2.AudioUtils.getLPCMChannelMappingForMencoder;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.startsWith;
public class TsMuxeRVideo extends Player {
private static final Logger logger = LoggerFactory.getLogger(TsMuxeRVideo.class);
private static final String COL_SPEC = "left:pref, 0:grow";
private static final String ROW_SPEC = "p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, p, 3dlu, 0:grow";
public static final String ID = "tsmuxer";
private PmsConfiguration configuration;
public TsMuxeRVideo(PmsConfiguration configuration) {
this.configuration = configuration;
}
@Override
public boolean excludeFormat(Format format) {
String extension = format.getMatchedExtension();
return extension != null
&& !extension.equals("mp4")
&& !extension.equals("mkv")
&& !extension.equals("ts")
&& !extension.equals("tp")
&& !extension.equals("m2ts")
&& !extension.equals("m2t")
&& !extension.equals("mpg")
&& !extension.equals("evo")
&& !extension.equals("mpeg")
&& !extension.equals("vob")
&& !extension.equals("m2v")
&& !extension.equals("mts")
&& !extension.equals("mov");
}
@Override
public PlayerPurpose getPurpose() {
return PlayerPurpose.VIDEO_FILE_PLAYER;
}
@Override
public String id() {
return ID;
}
@Override
public boolean isTimeSeekable() {
return true;
}
@Override
public String[] args() {
return null;
}
@Override
public String executable() {
return configuration.getTsmuxerPath();
}
@Override
public ProcessWrapper launchTranscode(
DLNAResource dlna,
DLNAMediaInfo media,
OutputParams params
) throws IOException {
final String filename = dlna.getSystemName();
setAudioAndSubs(filename, media, params, configuration);
PipeIPCProcess ffVideoPipe;
ProcessWrapperImpl ffVideo;
PipeIPCProcess ffAudioPipe[] = null;
ProcessWrapperImpl ffAudio[] = null;
String fps = media.getValidFps(false);
int width = media.getWidth();
int height = media.getHeight();
if (width < 320 || height < 240) {
width = -1;
height = -1;
}
String videoType = "V_MPEG4/ISO/AVC";
if (startsWith(media.getCodecV(), "mpeg2")) {
videoType = "V_MPEG-2";
}
if (this instanceof TsMuxeRAudio && media.getFirstAudioTrack() != null) {
String fakeFileName = writeResourceToFile("/resources/images/fake.jpg");
ffVideoPipe = new PipeIPCProcess(System.currentTimeMillis() + "fakevideo", System.currentTimeMillis() + "videoout", false, true);
String timeEndValue1 = "-t";
String timeEndValue2 = "" + params.timeend;
if (params.timeend < 1) {
timeEndValue1 = "-y";
timeEndValue2 = "-y";
}
String[] ffmpegLPCMextract = new String[] {
configuration.getFfmpegPath(),
timeEndValue1, timeEndValue2,
"-loop", "1",
"-i", fakeFileName,
"-qcomp", "0.6",
"-qmin", "10",
"-qmax", "51",
"-qdiff", "4",
"-me_range", "4",
"-f", "h264",
"-vcodec", "libx264",
"-an",
"-y",
ffVideoPipe.getInputPipe()
};
videoType = "V_MPEG4/ISO/AVC";
OutputParams ffparams = new OutputParams(configuration);
ffparams.maxBufferSize = 1;
ffVideo = new ProcessWrapperImpl(ffmpegLPCMextract, ffparams);
if (
filename.toLowerCase().endsWith(".flac") &&
media.getFirstAudioTrack().getBitsperSample() >= 24 &&
media.getFirstAudioTrack().getSampleRate() % 48000 == 0
) {
ffAudioPipe = new PipeIPCProcess[1];
ffAudioPipe[0] = new PipeIPCProcess(System.currentTimeMillis() + "flacaudio", System.currentTimeMillis() + "audioout", false, true);
String[] flacCmd = new String[] {
configuration.getFlacPath(),
"--output-name=" + ffAudioPipe[0].getInputPipe(),
"-d",
"-f",
"-F",
filename
};
ffparams = new OutputParams(configuration);
ffparams.maxBufferSize = 1;
ffAudio = new ProcessWrapperImpl[1];
ffAudio[0] = new ProcessWrapperImpl(flacCmd, ffparams);
} else {
ffAudioPipe = new PipeIPCProcess[1];
ffAudioPipe[0] = new PipeIPCProcess(System.currentTimeMillis() + "mlpaudio", System.currentTimeMillis() + "audioout", false, true);
String depth = "pcm_s16le";
String rate = "48000";
if (media.getFirstAudioTrack().getBitsperSample() >= 24) {
depth = "pcm_s24le";
}
if (media.getFirstAudioTrack().getSampleRate() > 48000) {
rate = "" + media.getFirstAudioTrack().getSampleRate();
}
String[] flacCmd = new String[] {
configuration.getFfmpegPath(),
"-i", filename,
"-ar", rate,
"-f", "wav",
"-acodec", depth,
"-y",
ffAudioPipe[0].getInputPipe()
};
ffparams = new OutputParams(configuration);
ffparams.maxBufferSize = 1;
ffAudio = new ProcessWrapperImpl[1];
ffAudio[0] = new ProcessWrapperImpl(flacCmd, ffparams);
}
} else {
params.waitbeforestart = 5000;
params.manageFastStart();
String mencoderPath = configuration.getMencoderPath();
ffVideoPipe = new PipeIPCProcess(System.currentTimeMillis() + "ffmpegvideo", System.currentTimeMillis() + "videoout", false, true);
// Special handling for evo files
String evoValue1 = "-quiet";
String evoValue2 = "-quiet";
if (filename.toLowerCase().endsWith(".evo")) {
evoValue1 = "-psprobe";
evoValue2 = "1000000";
}
String[] ffmpegLPCMextract = new String[] {
mencoderPath,
"-ss", params.timeseek > 0 ? "" + params.timeseek : "0",
params.stdin != null ? "-" : filename,
evoValue1, evoValue2,
"-really-quiet",
"-msglevel", "statusline=2",
"-ovc", "copy",
"-nosound",
"-mc", "0",
"-noskip",
"-of", "rawvideo",
"-o", ffVideoPipe.getInputPipe()
};
InputFile newInput = new InputFile();
newInput.setFilename(filename);
newInput.setPush(params.stdin);
if (media != null) {
// TODO useful only for logging
final boolean compat = media.isMediaparsed()
&& ((newInput != null && media.isVideoWithinH264LevelLimits(newInput, params.mediaRenderer))
|| !params.mediaRenderer.isH264Level41Limited());
if (!compat && params.mediaRenderer.isPS3()) {
logger.info("The video will not play or will show a black screen on the PS3...");
}
if (media.getH264AnnexB() != null && media.getH264AnnexB().length > 0) {
StreamModifier sm = new StreamModifier();
sm.setHeader(media.getH264AnnexB());
sm.setH264AnnexB(true);
ffVideoPipe.setModifier(sm);
}
}
OutputParams ffparams = new OutputParams(configuration);
ffparams.maxBufferSize = 1;
ffparams.stdin = params.stdin;
ffVideo = new ProcessWrapperImpl(ffmpegLPCMextract, ffparams);
int numAudioTracks = 1;
if (media.getAudioTracksList() != null && media.getAudioTracksList().size() > 1 && configuration.isMuxAllAudioTracks()) {
numAudioTracks = media.getAudioTracksList().size();
}
boolean singleMediaAudio = media.getAudioTracksList().size() <= 1;
if (params.aid != null) {
boolean ac3Remux;
boolean dtsRemux;
boolean pcm;
// Disable LPCM transcoding for MP4 container with non-H264 video as workaround for MEncoder's A/V sync bug
boolean mp4_with_non_h264 = (media.getContainer().equals("mp4") && !media.getCodecV().equals("h264"));
if (numAudioTracks <= 1) {
ffAudioPipe = new PipeIPCProcess[numAudioTracks];
ffAudioPipe[0] = new PipeIPCProcess(System.currentTimeMillis() + "ffmpegaudio01", System.currentTimeMillis() + "audioout", false, true);
/*
Disable AC-3 remux for stereo tracks with 384 kbits bitrate and PS3 renderer (PS3 FW bug?)
TODO check new firmwares
Commented out until we can find a way to detect when a video has an audio track that switches from 2 to 6 channels
because MEncoder can't handle those files, which are very common these days.
*/
// final boolean ps3_and_stereo_and_384_kbits = (params.mediaRenderer.isPS3() && params.aid.getAudioProperties().getNumberOfChannels() == 2)
// && (params.aid.getBitRate() > 370000 && params.aid.getBitRate() < 400000);
final boolean ps3_and_stereo_and_384_kbits = false;
ac3Remux = (params.aid.isAC3() && !ps3_and_stereo_and_384_kbits && configuration.isAudioRemuxAC3());
dtsRemux = configuration.isAudioEmbedDtsInPcm() && params.aid.isDTS() && params.mediaRenderer.isDTSPlayable();
pcm = configuration.isAudioUsePCM() &&
!mp4_with_non_h264 &&
(
params.aid.isLossless() ||
(params.aid.isDTS() && params.aid.getAudioProperties().getNumberOfChannels() <= 6) ||
params.aid.isTrueHD() ||
(
!configuration.isMencoderUsePcmForHQAudioOnly() &&
(
params.aid.isAC3() ||
params.aid.isMP3() ||
params.aid.isAAC() ||
params.aid.isVorbis() ||
// params.aid.isWMA() ||
params.aid.isMpegAudio()
)
)
) && params.mediaRenderer.isLPCMPlayable();
int channels;
if (ac3Remux) {
channels = params.aid.getAudioProperties().getNumberOfChannels(); // AC-3 remux
} else if (dtsRemux) {
channels = 2;
} else if (pcm) {
channels = params.aid.getAudioProperties().getNumberOfChannels();
} else {
channels = configuration.getAudioChannelCount(); // 5.1 max for AC-3 encoding
}
if (!ac3Remux && (dtsRemux || pcm)) {
// DTS remux or LPCM
StreamModifier sm = new StreamModifier();
sm.setPcm(pcm);
sm.setDtsEmbed(dtsRemux);
sm.setNbChannels(channels);
sm.setSampleFrequency(params.aid.getSampleRate() < 48000 ? 48000 : params.aid.getSampleRate());
sm.setBitsPerSample(16);
String mixer = null;
if (pcm && !dtsRemux) {
mixer = getLPCMChannelMappingForMencoder(params.aid);
}
ffmpegLPCMextract = new String[] {
mencoderPath,
"-ss", params.timeseek > 0 ? "" + params.timeseek : "0",
params.stdin != null ? "-" : filename,
evoValue1, evoValue2,
"-really-quiet",
"-msglevel", "statusline=2",
"-channels", "" + sm.getNbChannels(),
"-ovc", "copy",
"-of", "rawaudio",
"-mc", sm.isDtsEmbed() ? "0.1" : "0",
"-noskip",
"-oac", sm.isDtsEmbed() ? "copy" : "pcm",
isNotBlank(mixer) ? "-af" : "-quiet", isNotBlank(mixer) ? mixer : "-quiet",
singleMediaAudio ? "-quiet" : "-aid", singleMediaAudio ? "-quiet" : ("" + params.aid.getId()),
"-srate", "48000",
"-o", ffAudioPipe[0].getInputPipe()
};
// Use PCM trick when media renderer does not support DTS in MPEG
if (!params.mediaRenderer.isMuxDTSToMpeg()) {
ffAudioPipe[0].setModifier(sm);
}
} else {
// AC-3 remux or encoding
ffmpegLPCMextract = new String[] {
mencoderPath,
"-ss", params.timeseek > 0 ? "" + params.timeseek : "0",
params.stdin != null ? "-" : filename,
evoValue1, evoValue2,
"-really-quiet",
"-msglevel", "statusline=2",
"-channels", "" + channels,
"-ovc", "copy",
"-of", "rawaudio",
"-mc", "0",
"-noskip",
"-oac", (ac3Remux) ? "copy" : "lavc",
params.aid.isAC3() ? "-fafmttag" : "-quiet", params.aid.isAC3() ? "0x2000" : "-quiet",
"-lavcopts", "acodec=" + (configuration.isMencoderAc3Fixed() ? "ac3_fixed" : "ac3") + ":abitrate=" + CodecUtil.getAC3Bitrate(configuration, params.aid),
"-af", "lavcresample=48000",
"-srate", "48000",
singleMediaAudio ? "-quiet" : "-aid", singleMediaAudio ? "-quiet" : ("" + params.aid.getId()),
"-o", ffAudioPipe[0].getInputPipe()
};
}
ffparams = new OutputParams(configuration);
ffparams.maxBufferSize = 1;
ffparams.stdin = params.stdin;
ffAudio = new ProcessWrapperImpl[numAudioTracks];
ffAudio[0] = new ProcessWrapperImpl(ffmpegLPCMextract, ffparams);
} else {
ffAudioPipe = new PipeIPCProcess[numAudioTracks];
ffAudio = new ProcessWrapperImpl[numAudioTracks];
for (int i = 0; i < media.getAudioTracksList().size(); i++) {
DLNAMediaAudio audio = media.getAudioTracksList().get(i);
ffAudioPipe[i] = new PipeIPCProcess(System.currentTimeMillis() + "ffmpeg" + i, System.currentTimeMillis() + "audioout" + i, false, true);
/*
Disable AC-3 remux for stereo tracks with 384 kbits bitrate and PS3 renderer (PS3 FW bug?)
TODO check new firmwares
Commented out until we can find a way to detect when a video has an audio track that switches from 2 to 6 channels
because MEncoder can't handle those files, which are very common these days.
*/
// final boolean ps3_and_stereo_and_384_kbits = (params.mediaRenderer.isPS3() && audio.getAudioProperties().getNumberOfChannels() == 2)
// && (audio.getBitRate() > 370000 && audio.getBitRate() < 400000);
final boolean ps3_and_stereo_and_384_kbits = false;
ac3Remux = audio.isAC3() && !ps3_and_stereo_and_384_kbits && configuration.isAudioRemuxAC3();
dtsRemux = configuration.isAudioEmbedDtsInPcm() && audio.isDTS() && params.mediaRenderer.isDTSPlayable();
pcm = configuration.isAudioUsePCM() &&
!mp4_with_non_h264 &&
(
audio.isLossless() ||
(audio.isDTS() && audio.getAudioProperties().getNumberOfChannels() <= 6) ||
audio.isTrueHD() ||
(
!configuration.isMencoderUsePcmForHQAudioOnly() &&
(
audio.isAC3() ||
audio.isMP3() ||
audio.isAAC() ||
audio.isVorbis() ||
// audio.isWMA() ||
audio.isMpegAudio()
)
)
) && params.mediaRenderer.isLPCMPlayable();
int channels;
if (ac3Remux) {
channels = audio.getAudioProperties().getNumberOfChannels(); // AC-3 remux
} else if (dtsRemux) {
channels = 2;
} else if (pcm) {
channels = audio.getAudioProperties().getNumberOfChannels();
} else {
channels = configuration.getAudioChannelCount(); // 5.1 max for AC-3 encoding
}
if (!ac3Remux && (dtsRemux || pcm)) {
// DTS remux or LPCM
StreamModifier sm = new StreamModifier();
sm.setPcm(pcm);
sm.setDtsEmbed(dtsRemux);
sm.setNbChannels(channels);
sm.setSampleFrequency(audio.getSampleRate() < 48000 ? 48000 : audio.getSampleRate());
sm.setBitsPerSample(16);
if (!params.mediaRenderer.isMuxDTSToMpeg()) {
ffAudioPipe[i].setModifier(sm);
}
String mixer = null;
if (pcm && !dtsRemux) {
mixer = getLPCMChannelMappingForMencoder(audio);
}
ffmpegLPCMextract = new String[]{
mencoderPath,
"-ss", params.timeseek > 0 ? "" + params.timeseek : "0",
params.stdin != null ? "-" : filename,
evoValue1, evoValue2,
"-really-quiet",
"-msglevel", "statusline=2",
"-channels", "" + sm.getNbChannels(),
"-ovc", "copy",
"-of", "rawaudio",
"-mc", sm.isDtsEmbed() ? "0.1" : "0",
"-noskip",
"-oac", sm.isDtsEmbed() ? "copy" : "pcm",
isNotBlank(mixer) ? "-af" : "-quiet", isNotBlank(mixer) ? mixer : "-quiet",
singleMediaAudio ? "-quiet" : "-aid", singleMediaAudio ? "-quiet" : ("" + audio.getId()),
"-srate", "48000",
"-o", ffAudioPipe[i].getInputPipe()
};
} else {
// AC-3 remux or encoding
ffmpegLPCMextract = new String[]{
mencoderPath,
"-ss", params.timeseek > 0 ? "" + params.timeseek : "0",
params.stdin != null ? "-" : filename,
evoValue1, evoValue2,
"-really-quiet",
"-msglevel", "statusline=2",
"-channels", "" + channels,
"-ovc", "copy",
"-of", "rawaudio",
"-mc", "0",
"-noskip",
"-oac", (ac3Remux) ? "copy" : "lavc",
audio.isAC3() ? "-fafmttag" : "-quiet", audio.isAC3() ? "0x2000" : "-quiet",
"-lavcopts", "acodec=" + (configuration.isMencoderAc3Fixed() ? "ac3_fixed" : "ac3") + ":abitrate=" + CodecUtil.getAC3Bitrate(configuration, audio),
"-af", "lavcresample=48000",
"-srate", "48000",
singleMediaAudio ? "-quiet" : "-aid", singleMediaAudio ? "-quiet" : ("" + audio.getId()),
"-o", ffAudioPipe[i].getInputPipe()
};
}
ffparams = new OutputParams(configuration);
ffparams.maxBufferSize = 1;
ffparams.stdin = params.stdin;
ffAudio[i] = new ProcessWrapperImpl(ffmpegLPCMextract, ffparams);
}
}
}
}
File f = new File(configuration.getTempFolder(), "pms-tsmuxer.meta");
params.log = false;
PrintWriter pw = new PrintWriter(f);
pw.print("MUXOPT --no-pcr-on-video-pid");
pw.print(" --new-audio-pes");
pw.print(" --no-asyncio");
pw.print(" --vbr");
pw.println(" --vbv-len=500");
String videoparams = "level=4.1, insertSEI, contSPS, track=1";
if (this instanceof TsMuxeRAudio) {
videoparams = "track=224";
}
if (configuration.isFix25FPSAvMismatch()) {
fps = "25";
}
pw.println(videoType + ", \"" + ffVideoPipe.getOutputPipe() + "\", " + (fps != null ? ("fps=" + fps + ", ") : "") + (width != -1 ? ("video-width=" + width + ", ") : "") + (height != -1 ? ("video-height=" + height + ", ") : "") + videoparams);
// Disable LPCM transcoding for MP4 container with non-H264 video as workaround for mencoder's A/V sync bug
boolean mp4_with_non_h264 = (media.getContainer().equals("mp4") && !media.getCodecV().equals("h264"));
if (ffAudioPipe != null && ffAudioPipe.length == 1) {
String timeshift = "";
boolean ac3Remux;
boolean dtsRemux;
boolean pcm;
/*
Disable AC-3 remux for stereo tracks with 384 kbits bitrate and PS3 renderer (PS3 FW bug?)
TODO check new firmwares
Commented out until we can find a way to detect when a video has an audio track that switches from 2 to 6 channels
because MEncoder can't handle those files, which are very common these days.
*/
// final boolean ps3_and_stereo_and_384_kbits = (params.mediaRenderer.isPS3() && params.aid.getAudioProperties().getNumberOfChannels() == 2)
// && (params.aid.getBitRate() > 370000 && params.aid.getBitRate() < 400000);
final boolean ps3_and_stereo_and_384_kbits = false;
ac3Remux = params.aid.isAC3() && !ps3_and_stereo_and_384_kbits && configuration.isAudioRemuxAC3();
dtsRemux = configuration.isAudioEmbedDtsInPcm() && params.aid.isDTS() && params.mediaRenderer.isDTSPlayable();
pcm = configuration.isAudioUsePCM() &&
!mp4_with_non_h264 &&
(
params.aid.isLossless() ||
(params.aid.isDTS() && params.aid.getAudioProperties().getNumberOfChannels() <= 6) ||
params.aid.isTrueHD() ||
(
!configuration.isMencoderUsePcmForHQAudioOnly() &&
(
params.aid.isAC3() ||
params.aid.isMP3() ||
params.aid.isAAC() ||
params.aid.isVorbis() ||
// params.aid.isWMA() ||
params.aid.isMpegAudio()
)
)
) && params.mediaRenderer.isLPCMPlayable();
String type = "A_AC3";
if (ac3Remux) {
// AC-3 remux takes priority
type = "A_AC3";
} else {
if ( pcm || this instanceof TsMuxeRAudio)
{
type = "A_LPCM";
}
if ( dtsRemux || this instanceof TsMuxeRAudio)
{
type = "A_LPCM";
if (params.mediaRenderer.isMuxDTSToMpeg()) {
type = "A_DTS";
}
}
}
if (params.aid != null && params.aid.getAudioProperties().getAudioDelay() != 0 && params.timeseek == 0) {
timeshift = "timeshift=" + params.aid.getAudioProperties().getAudioDelay() + "ms, ";
}
pw.println(type + ", \"" + ffAudioPipe[0].getOutputPipe() + "\", " + timeshift + "track=2");
} else if (ffAudioPipe != null) {
for (int i = 0; i < media.getAudioTracksList().size(); i++) {
DLNAMediaAudio lang = media.getAudioTracksList().get(i);
String timeshift = "";
boolean ac3Remux;
boolean dtsRemux;
boolean pcm;
/*
Disable AC-3 remux for stereo tracks with 384 kbits bitrate and PS3 renderer (PS3 FW bug?)
TODO check new firmwares
Commented out until we can find a way to detect when a video has an audio track that switches from 2 to 6 channels
because MEncoder can't handle those files, which are very common these days.
*/
// final boolean ps3_and_stereo_and_384_kbits = (params.mediaRenderer.isPS3() && lang.getAudioProperties().getNumberOfChannels() == 2)
// && (lang.getBitRate() > 370000 && lang.getBitRate() < 400000);
final boolean ps3_and_stereo_and_384_kbits = false;
ac3Remux = lang.isAC3() && !ps3_and_stereo_and_384_kbits && configuration.isAudioRemuxAC3();
dtsRemux = configuration.isAudioEmbedDtsInPcm() && lang.isDTS() && params.mediaRenderer.isDTSPlayable();
pcm = configuration.isAudioUsePCM() &&
!mp4_with_non_h264 &&
(
lang.isLossless() ||
(lang.isDTS() && lang.getAudioProperties().getNumberOfChannels() <= 6) ||
lang.isTrueHD() ||
(
!configuration.isMencoderUsePcmForHQAudioOnly() &&
(
params.aid.isAC3() ||
params.aid.isMP3() ||
params.aid.isAAC() ||
params.aid.isVorbis() ||
// params.aid.isWMA() ||
params.aid.isMpegAudio()
)
)
) && params.mediaRenderer.isLPCMPlayable();
String type = "A_AC3";
if (ac3Remux) {
// AC-3 remux takes priority
type = "A_AC3";
} else {
if (pcm)
{
type = "A_LPCM";
}
if (dtsRemux)
{
type = "A_LPCM";
if (params.mediaRenderer.isMuxDTSToMpeg()) {
type = "A_DTS";
}
}
}
if (lang.getAudioProperties().getAudioDelay() != 0 && params.timeseek == 0) {
timeshift = "timeshift=" + lang.getAudioProperties().getAudioDelay() + "ms, ";
}
pw.println(type + ", \"" + ffAudioPipe[i].getOutputPipe() + "\", " + timeshift + "track=" + (2 + i));
}
}
pw.close();
PipeProcess tsPipe = new PipeProcess(System.currentTimeMillis() + "tsmuxerout.ts");
String[] cmdArray = new String[]{
executable(),
f.getAbsolutePath(),
tsPipe.getInputPipe()
};
cmdArray = finalizeTranscoderArgs(
filename,
dlna,
media,
params,
cmdArray
);
ProcessWrapperImpl p = new ProcessWrapperImpl(cmdArray, params);
params.maxBufferSize = 100;
params.input_pipes[0] = tsPipe;
params.stdin = null;
ProcessWrapper pipe_process = tsPipe.getPipeProcess();
p.attachProcess(pipe_process);
pipe_process.runInNewThread();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
tsPipe.deleteLater();
ProcessWrapper ff_pipe_process = ffVideoPipe.getPipeProcess();
p.attachProcess(ff_pipe_process);
ff_pipe_process.runInNewThread();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
ffVideoPipe.deleteLater();
p.attachProcess(ffVideo);
ffVideo.runInNewThread();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
if (ffAudioPipe != null && params.aid != null) {
for (int i = 0; i < ffAudioPipe.length; i++) {
ff_pipe_process = ffAudioPipe[i].getPipeProcess();
p.attachProcess(ff_pipe_process);
ff_pipe_process.runInNewThread();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
ffAudioPipe[i].deleteLater();
p.attachProcess(ffAudio[i]);
ffAudio[i].runInNewThread();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
p.runInNewThread();
return p;
}
/**
* Write the resource "/resources/images/fake.jpg" to a physical file on disk.
*
* @return The filename of the file on disk.
*/
private String writeResourceToFile(String resourceName) {
String outputFileName = resourceName.substring(resourceName.lastIndexOf("/") + 1);
try {
outputFileName = configuration.getTempFolder() + "/" + outputFileName;
} catch (IOException e) {
logger.warn("Failure to determine temporary folder.", e);
}
File outputFile = new File(outputFileName);
// Copy the resource file only once
if (!outputFile.exists()) {
final URL resourceUrl = getClass().getClassLoader().getResource(resourceName);
byte[] buffer = new byte[1024];
int byteCount;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = resourceUrl.openStream();
outputStream = new FileOutputStream(outputFileName);
while ((byteCount = inputStream.read(buffer)) >= 0) {
outputStream.write(buffer, 0, byteCount);
}
} catch (final IOException e) {
logger.error("Failure on saving the embedded resource " + resourceName + " to the file " + outputFile.getAbsolutePath(), e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (final IOException e) {
logger.warn("Problem closing an input stream while reading data from the embedded resource " + resourceName, e);
}
}
if (outputStream != null) {
try {
outputStream.flush();
outputStream.close();
} catch (final IOException e) {
logger.warn("Problem closing the output stream while writing the file " + outputFile.getAbsolutePath(), e);
}
}
}
}
return outputFileName;
}
@Override
public String mimeType() {
return "video/mpeg";
}
@Override
public String name() {
return "tsMuxeR";
}
@Override
public int type() {
return Format.VIDEO;
}
private JCheckBox tsmuxerforcefps;
private JCheckBox muxallaudiotracks;
@Override
public JComponent config() {
// Apply the orientation for the locale
Locale locale = new Locale(configuration.getLanguage());
ComponentOrientation orientation = ComponentOrientation.getOrientation(locale);
String colSpec = FormLayoutUtil.getColSpec(COL_SPEC, orientation);
FormLayout layout = new FormLayout(colSpec, ROW_SPEC);
PanelBuilder builder = new PanelBuilder(layout);
CellConstraints cc = new CellConstraints();
JComponent cmp = builder.addSeparator(Messages.getString("TSMuxerVideo.3"), FormLayoutUtil.flip(cc.xyw(2, 1, 1), colSpec, orientation));
cmp = (JComponent) cmp.getComponent(0);
cmp.setFont(cmp.getFont().deriveFont(Font.BOLD));
tsmuxerforcefps = new JCheckBox(Messages.getString("TSMuxerVideo.2"));
tsmuxerforcefps.setContentAreaFilled(false);
if (configuration.isTsmuxerForceFps()) {
tsmuxerforcefps.setSelected(true);
}
tsmuxerforcefps.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
configuration.setTsmuxerForceFps(e.getStateChange() == ItemEvent.SELECTED);
}
});
builder.add(tsmuxerforcefps, FormLayoutUtil.flip(cc.xy(2, 3), colSpec, orientation));
muxallaudiotracks = new JCheckBox(Messages.getString("TSMuxerVideo.19"));
muxallaudiotracks.setContentAreaFilled(false);
if (configuration.isMuxAllAudioTracks()) {
muxallaudiotracks.setSelected(true);
}
muxallaudiotracks.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
configuration.setMuxAllAudioTracks(e.getStateChange() == ItemEvent.SELECTED);
}
});
builder.add(muxallaudiotracks, FormLayoutUtil.flip(cc.xy(2, 5), colSpec, orientation));
JPanel panel = builder.getPanel();
// Apply the orientation to the panel and all components in it
panel.applyComponentOrientation(orientation);
return panel;
}
@Override
public boolean isInternalSubtitlesSupported() {
return false;
}
@Override
public boolean isExternalSubtitlesSupported() {
return false;
}
@Override
public boolean isPlayerCompatible(RendererConfiguration mediaRenderer) {
return mediaRenderer != null && mediaRenderer.isMuxH264MpegTS();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCompatible(DLNAResource dlna) {
if (!(
PlayerUtil.isVideo(dlna, Format.Identifier.MKV) ||
PlayerUtil.isVideo(dlna, Format.Identifier.MPG)
)) {
return false;
}
/*
* Notes:
*
* 1) isCompatible is used for two separate tasks: 1) selection
* of #--TRANSCODE--# folder entries (PlayerFactory.getPlayers)
* and 2) assignment of the default player for a resource
* (PlayerFactory.getPlayer)
*
* 2) for resources that aren't in the #--TRANSCODE--# folder, it's understood
* that tsMuxeR will only stream the first audio track and that it won't/can't
* stream subtitles, so no need to check either
*/
if (dlna.isNoName()) { // #--TRANSCODE--# folder entry
DLNAMediaAudio dlnaMediaAudio = dlna.getMediaAudio();
// media is non-null and audioTrackList is non-empty by definition if
// mediaAudio has been set (see FileTranscodeVirtualFolder)
if ((dlnaMediaAudio != null) && !dlnaMediaAudio.toString().equals(dlna.getMedia().getFirstAudioTrack().toString())) {
// PMS only supports playback of the first audio track for tsMuxeR
return false;
}
// XXX why is tsMuxeR compatible if the subtitle language is null?
if ((dlna.getMediaSubtitle() != null) && (dlna.getMediaSubtitle().getLang() != null)) {
return false;
}
}
return true;
}
}