Search in sources :

Example 1 with URLResult

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;
}
Also used : PipeProcess(net.pms.io.PipeProcess) ArrayList(java.util.ArrayList) URLResult(net.pms.external.URLResolver.URLResult) ProcessWrapper(net.pms.io.ProcessWrapper) PmsConfiguration(net.pms.configuration.PmsConfiguration) RendererConfiguration(net.pms.configuration.RendererConfiguration) ProcessWrapperImpl(net.pms.io.ProcessWrapperImpl)

Aggregations

ArrayList (java.util.ArrayList)1 PmsConfiguration (net.pms.configuration.PmsConfiguration)1 RendererConfiguration (net.pms.configuration.RendererConfiguration)1 URLResult (net.pms.external.URLResolver.URLResult)1 PipeProcess (net.pms.io.PipeProcess)1 ProcessWrapper (net.pms.io.ProcessWrapper)1 ProcessWrapperImpl (net.pms.io.ProcessWrapperImpl)1