use of net.pms.external.URLResolver.URLResult in project UniversalMediaServer by UniversalMediaServer.
the class modAwareHashMap method launchTranscode.
@Override
public ProcessWrapper launchTranscode(DLNAResource dlna, DLNAMediaInfo media, OutputParams params) throws IOException {
params.minBufferSize = params.minFileSize;
params.secondread_minsize = 100000;
// Use device-specific pms conf
PmsConfiguration prev = configuration;
configuration = (DeviceConfiguration) params.mediaRenderer;
RendererConfiguration renderer = params.mediaRenderer;
String filename = dlna.getFileName();
setAudioAndSubs(filename, media, params);
// XXX work around an ffmpeg bug: http://ffmpeg.org/trac/ffmpeg/ticket/998
if (filename.startsWith("mms:")) {
filename = "mmsh:" + filename.substring(4);
}
// check if we have modifier for this url
String r = replacements.match(filename);
if (r != null) {
filename = filename.replaceAll(r, replacements.get(r));
LOGGER.debug("modified url: " + filename);
}
FFmpegOptions customOptions = new FFmpegOptions();
// Gather custom options from various sources in ascending priority:
// - automatic options
String match = autoOptions.match(filename);
if (match != null) {
List<String> opts = autoOptions.get(match);
if (opts != null) {
customOptions.addAll(opts);
}
}
// - (http) header options
if (params.header != null && params.header.length > 0) {
String hdr = new String(params.header);
customOptions.addAll(parseOptions(hdr));
}
// - attached options
String attached = (String) dlna.getAttachment(ID);
if (attached != null) {
customOptions.addAll(parseOptions(attached));
}
// - renderer options
String ffmpegOptions = renderer.getCustomFFmpegOptions();
if (StringUtils.isNotEmpty(ffmpegOptions)) {
customOptions.addAll(parseOptions(ffmpegOptions));
}
// Build the command line
List<String> cmdList = new ArrayList<>();
if (!dlna.isURLResolved()) {
URLResult r1 = ExternalFactory.resolveURL(filename);
if (r1 != null) {
if (r1.precoder != null) {
filename = "-";
if (Platform.isWindows()) {
cmdList.add("cmd.exe");
cmdList.add("/C");
}
cmdList.addAll(r1.precoder);
cmdList.add("|");
} else {
if (StringUtils.isNotEmpty(r1.url)) {
filename = r1.url;
}
}
if (r1.args != null && r1.args.size() > 0) {
customOptions.addAll(r1.args);
}
}
}
cmdList.add(executable());
// XXX squashed bug - without this, ffmpeg hangs waiting for a confirmation
// that it can write to a file that already exists i.e. the named pipe
cmdList.add("-y");
cmdList.add("-loglevel");
if (LOGGER.isTraceEnabled()) {
// Set -loglevel in accordance with LOGGER setting
// Could be changed to "verbose" or "debug" if "info" level is not enough
cmdList.add("info");
} else {
cmdList.add("warning");
}
/*
* FFmpeg uses multithreading by default, so provided that the
* user has not disabled FFmpeg multithreading and has not
* chosen to use more or less threads than are available, do not
* specify how many cores to use.
*/
int nThreads = 1;
if (configuration.isFfmpegMultithreading()) {
if (Runtime.getRuntime().availableProcessors() == configuration.getNumberOfCpuCores()) {
nThreads = 0;
} else {
nThreads = configuration.getNumberOfCpuCores();
}
}
// Decoder threads
if (nThreads > 0) {
cmdList.add("-threads");
cmdList.add("" + nThreads);
}
// Add global and input-file custom options, if any
if (!customOptions.isEmpty()) {
customOptions.transferGlobals(cmdList);
customOptions.transferInputFileOptions(cmdList);
}
if (params.timeseek > 0) {
cmdList.add("-ss");
cmdList.add("" + (int) params.timeseek);
}
cmdList.add("-i");
cmdList.add(filename);
cmdList.addAll(getVideoFilterOptions(dlna, media, params));
// Encoder threads
if (nThreads > 0) {
cmdList.add("-threads");
cmdList.add("" + nThreads);
}
// Add the output options (-f, -c:a, -c:v, etc.)
// Now that inputs and filtering are complete, see if we should
// give the renderer the final say on the command
boolean override = false;
if (renderer instanceof RendererConfiguration.OutputOverride) {
override = ((RendererConfiguration.OutputOverride) renderer).getOutputOptions(cmdList, dlna, this, params);
}
if (!override) {
cmdList.addAll(getVideoTranscodeOptions(dlna, media, params));
// Add video bitrate options
cmdList.addAll(getVideoBitrateOptions(dlna, media, params));
// Add audio bitrate options
cmdList.addAll(getAudioBitrateOptions(dlna, media, params));
// Add any remaining custom options
if (!customOptions.isEmpty()) {
customOptions.transferAll(cmdList);
}
}
// Set up the process
// basename of the named pipe:
String fifoName = String.format("ffmpegwebvideo_%d_%d", Thread.currentThread().getId(), System.currentTimeMillis());
// This process wraps the command that creates the named pipe
PipeProcess pipe = new PipeProcess(fifoName);
// delete the named pipe later; harmless if it isn't created
pipe.deleteLater();
ProcessWrapper mkfifo_process = pipe.getPipeProcess();
/**
* 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();
params.input_pipes[0] = pipe;
// Output file
cmdList.add(pipe.getInputPipe());
// Convert the command list to an array
String[] cmdArray = new String[cmdList.size()];
cmdList.toArray(cmdArray);
// Hook to allow plugins to customize this command line
cmdArray = finalizeTranscoderArgs(filename, dlna, media, params, cmdArray);
// Now launch FFmpeg
ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, params);
// Better late than never
parseMediaInfo(filename, dlna, pw);
// Clean up the mkfifo process when the transcode ends
pw.attachProcess(mkfifo_process);
// Give the mkfifo process a little time
try {
Thread.sleep(300);
} catch (InterruptedException e) {
LOGGER.error("Thread interrupted while waiting for named pipe to be created", e);
}
// Launch the transcode command...
pw.runInNewThread();
// ...and wait briefly to allow it to start
try {
Thread.sleep(200);
} catch (InterruptedException e) {
LOGGER.error("Thread interrupted while waiting for transcode to start", e);
}
configuration = prev;
return pw;
}
Aggregations