Search in sources :

Example 11 with PmsConfiguration

use of net.pms.configuration.PmsConfiguration 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)

Example 12 with PmsConfiguration

use of net.pms.configuration.PmsConfiguration in project UniversalMediaServer by UniversalMediaServer.

the class MEncoderVideo method addMaximumBitrateConstraints.

/**
 * Note: This is not exact. The bitrate can go above this but it is generally pretty good.
 *
 * @return The maximum bitrate the video should be along with the buffer size using MEncoder vars
 */
private String addMaximumBitrateConstraints(String encodeSettings, DLNAMediaInfo media, String quality, RendererConfiguration mediaRenderer, String audioType) {
    // Use device-specific pms conf
    PmsConfiguration configuration = PMS.getConfiguration(mediaRenderer);
    int[] defaultMaxBitrates = getVideoBitrateConfig(configuration.getMaximumBitrate());
    int[] rendererMaxBitrates = new int[2];
    if (isNotEmpty(mediaRenderer.getMaxVideoBitrate())) {
        rendererMaxBitrates = getVideoBitrateConfig(mediaRenderer.getMaxVideoBitrate());
    }
    // Give priority to the renderer's maximum bitrate setting over the user's setting
    if (rendererMaxBitrates[0] > 0 && rendererMaxBitrates[0] < defaultMaxBitrates[0]) {
        LOGGER.trace("Using video bitrate limit from {} configuration ({} Mb/s) because " + "it is lower than the general configuration bitrate limit ({} Mb/s)", mediaRenderer.getRendererName(), rendererMaxBitrates[0], defaultMaxBitrates[0]);
        defaultMaxBitrates = rendererMaxBitrates;
    } else {
        LOGGER.trace("Using video bitrate limit from the general configuration ({} Mb/s)", defaultMaxBitrates[0]);
    }
    if (mediaRenderer.getCBRVideoBitrate() == 0 && !quality.contains("vrc_buf_size") && !quality.contains("vrc_maxrate") && !quality.contains("vbitrate")) {
        // Convert value from Mb to Kb
        defaultMaxBitrates[0] = 1000 * defaultMaxBitrates[0];
        if (mediaRenderer.isHalveBitrate()) {
            defaultMaxBitrates[0] /= 2;
            LOGGER.trace("Halving the video bitrate limit to {} kb/s", defaultMaxBitrates[0]);
        }
        int bufSize = 1835;
        boolean bitrateLevel41Limited = false;
        boolean isXboxOneWebVideo = mediaRenderer.isXboxOne() && purpose() == VIDEO_WEBSTREAM_PLAYER;
        /**
         * Although the maximum bitrate for H.264 Level 4.1 is
         * officially 50,000 kbit/s, some 4.1-capable renderers
         * like the PS3 stutter when video exceeds roughly 31,250
         * kbit/s.
         *
         * We also apply the correct buffer size in this section.
         */
        if ((mediaRenderer.isTranscodeToH264() || mediaRenderer.isTranscodeToH265()) && !isXboxOneWebVideo) {
            if (mediaRenderer.isH264Level41Limited() && defaultMaxBitrates[0] > 31250) {
                defaultMaxBitrates[0] = 31250;
                bitrateLevel41Limited = true;
                LOGGER.trace("Adjusting the video bitrate limit to the H.264 Level 4.1-safe value of 31250 kb/s");
            }
            bufSize = defaultMaxBitrates[0];
        } else {
            if (media.isHDVideo()) {
                bufSize = defaultMaxBitrates[0] / 3;
            }
            if (bufSize > 7000) {
                bufSize = 7000;
            }
            if (defaultMaxBitrates[1] > 0) {
                bufSize = defaultMaxBitrates[1];
            }
            if (mediaRenderer.isDefaultVBVSize() && rendererMaxBitrates[1] == 0) {
                bufSize = 1835;
            }
        }
        if (!bitrateLevel41Limited) {
            // Make room for audio
            switch(audioType) {
                case "pcm":
                    defaultMaxBitrates[0] -= 4600;
                    break;
                case "dts":
                    defaultMaxBitrates[0] -= 1510;
                    break;
                case "aac":
                case "ac3":
                    defaultMaxBitrates[0] -= configuration.getAudioBitrate();
                    break;
                default:
                    break;
            }
            // Round down to the nearest Mb
            defaultMaxBitrates[0] = defaultMaxBitrates[0] / 1000 * 1000;
            LOGGER.trace("Adjusting the video bitrate limit to {} kb/s to make room for audio", defaultMaxBitrates[0]);
        }
        encodeSettings += ":vrc_maxrate=" + defaultMaxBitrates[0] + ":vrc_buf_size=" + bufSize;
    }
    return encodeSettings;
}
Also used : PmsConfiguration(net.pms.configuration.PmsConfiguration)

Example 13 with PmsConfiguration

use of net.pms.configuration.PmsConfiguration in project UniversalMediaServer by UniversalMediaServer.

the class DCRaw method getThumbnail.

/**
 * Extracts or generates a thumbnail for {@code fileName}.
 *
 * @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.
 * @return A byte array containing the thumbnail or {@code null}.
 * @throws IOException if an IO error occurs.
 */
@Override
public byte[] getThumbnail(OutputParams params, String fileName, ImageInfo imageInfo) {
    boolean trace = LOGGER.isTraceEnabled();
    if (trace) {
        LOGGER.trace("Extracting thumbnail from \"{}\" with DCRaw", fileName);
    }
    if (params == null) {
        params = new OutputParams(PMS.getConfiguration());
    }
    // Use device-specific pms conf
    PmsConfiguration configuration = PMS.getConfiguration(params);
    params.log = false;
    // This is a wild guess at a decent buffer size for an embedded thumbnail.
    // Every time the buffer has to grow, the whole buffer must be copied in memory.
    params.outputByteArrayStreamBufferSize = 150000;
    // First try to get the embedded thumbnail
    String[] cmdArray = new String[6];
    cmdArray[0] = configuration.getDCRawPath();
    cmdArray[1] = "-e";
    cmdArray[2] = "-c";
    cmdArray[3] = "-M";
    cmdArray[4] = "-w";
    cmdArray[5] = fileName;
    ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, true, params, false, true);
    pw.runInSameThread();
    byte[] bytes = pw.getOutputByteArray().toByteArray();
    List<String> results = pw.getResults();
    if (bytes.length > 0) {
        // DCRaw doesn't seem to apply Exif Orientation to embedded thumbnails, handle it
        boolean isJPEG = (bytes[0] & 0xFF) == 0xFF && (bytes[1] & 0xFF) == 0xD8;
        ExifOrientation thumbnailOrientation = null;
        Dimension jpegResolution = null;
        int exifOrientationOffset = -1;
        if (isJPEG) {
            try {
                ByteArrayReader reader = new ByteArrayReader(bytes);
                exifOrientationOffset = ImagesUtil.getJPEGExifIFDTagOffset(0x112, reader);
                jpegResolution = ImagesUtil.getJPEGResolution(reader);
            } catch (IOException e) {
                exifOrientationOffset = -1;
                LOGGER.debug("Unexpected error while trying to find Exif orientation offset in embedded thumbnail for \"{}\": {}", fileName, e.getMessage());
                LOGGER.trace("", e);
            }
            if (exifOrientationOffset > 0) {
                thumbnailOrientation = ExifOrientation.typeOf(bytes[exifOrientationOffset]);
            } else {
                LOGGER.debug("Couldn't find Exif orientation in the thumbnail extracted from \"{}\"", fileName);
            }
        }
        ExifOrientation imageOrientation = imageInfo instanceof ExifInfo ? ((ExifInfo) imageInfo).getOriginalExifOrientation() : null;
        if (imageOrientation != null && imageOrientation != thumbnailOrientation) {
            if (thumbnailOrientation != null) {
                if (imageInfo.getWidth() > 0 && imageInfo.getHeight() > 0 && jpegResolution != null && jpegResolution.getWidth() > 0 && jpegResolution.getHeight() > 0) {
                    // Try to determine which orientation to trust
                    double imageAspect, thumbnailAspect;
                    if (ImagesUtil.isExifAxesSwapNeeded(imageOrientation)) {
                        imageAspect = (double) imageInfo.getHeight() / imageInfo.getWidth();
                    } else {
                        imageAspect = (double) imageInfo.getWidth() / imageInfo.getHeight();
                    }
                    if (ImagesUtil.isExifAxesSwapNeeded(thumbnailOrientation)) {
                        thumbnailAspect = (double) jpegResolution.getHeight() / jpegResolution.getWidth();
                    } else {
                        thumbnailAspect = (double) jpegResolution.getWidth() / jpegResolution.getHeight();
                    }
                    if (Math.abs(imageAspect - thumbnailAspect) > 0.001d) {
                        // The image and the thumbnail seems to have different aspect ratios, use that of the image
                        bytes[exifOrientationOffset] = (byte) imageOrientation.getValue();
                    }
                }
            } else if (imageOrientation != ExifOrientation.TOP_LEFT) {
                // Apply the orientation to the thumbnail
                try {
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    Thumbnails.of(new ByteArrayInputStream(bytes)).scale(1.0d).addFilter(ExifFilterUtils.getFilterForOrientation(imageOrientation.getThumbnailatorOrientation())).outputFormat(// PNG here to avoid further degradation from rotation
                    "PNG").outputQuality(1.0f).toOutputStream(outputStream);
                    bytes = outputStream.toByteArray();
                } catch (IOException e) {
                    LOGGER.error("Unexpected error when trying to rotate thumbnail for \"{}\" - cancelling rotation: {}", fileName, e.getMessage());
                    LOGGER.trace("", e);
                }
            }
        }
    }
    if (bytes.length == 0 || !results.isEmpty() && results.get(0).contains("has no thumbnail")) {
        // No embedded thumbnail retrieved, generate thumbnail from the actual file
        if (trace) {
            LOGGER.trace("No embedded thumbnail found in \"{}\", " + "trying to generate thumbnail from the image itself", fileName);
        }
        params.outputByteArrayStreamBufferSize = imageInfo != null && imageInfo.getSize() != ImageInfo.SIZE_UNKNOWN ? (int) imageInfo.getSize() / 4 : 500000;
        cmdArray[1] = "-h";
        pw = new ProcessWrapperImpl(cmdArray, true, params);
        pw.runInSameThread();
        bytes = pw.getOutputByteArray().toByteArray();
    }
    if (trace && (bytes == null || bytes.length == 0)) {
        LOGGER.trace("Failed to generate thumbnail with DCRaw for image \"{}\"", fileName);
    }
    return bytes != null && bytes.length > 0 ? bytes : null;
}
Also used : Dimension(java.awt.Dimension) IOException(java.io.IOException) ByteArrayOutputStream(java.io.ByteArrayOutputStream) PmsConfiguration(net.pms.configuration.PmsConfiguration) ByteArrayInputStream(java.io.ByteArrayInputStream) ExifInfo(net.pms.image.ExifInfo) OutputParams(net.pms.io.OutputParams) ExifOrientation(net.pms.image.ExifOrientation) ByteArrayReader(com.drew.lang.ByteArrayReader) ProcessWrapperImpl(net.pms.io.ProcessWrapperImpl)

Example 14 with PmsConfiguration

use of net.pms.configuration.PmsConfiguration in project UniversalMediaServer by UniversalMediaServer.

the class FFmpegAudio method launchTranscode.

@Override
public synchronized ProcessWrapper launchTranscode(DLNAResource dlna, DLNAMediaInfo media, OutputParams params) throws IOException {
    PmsConfiguration prev = configuration;
    // Use device-specific pms conf
    configuration = (DeviceConfiguration) params.mediaRenderer;
    final String filename = dlna.getFileName();
    params.maxBufferSize = configuration.getMaxAudioBuffer();
    params.waitbeforestart = 2000;
    params.manageFastStart();
    /*
		 * 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();
        }
    }
    List<String> cmdList = new ArrayList<>();
    cmdList.add(executable());
    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");
    }
    if (params.timeseek > 0) {
        cmdList.add("-ss");
        cmdList.add("" + params.timeseek);
    }
    // Decoder threads
    if (nThreads > 0) {
        cmdList.add("-threads");
        cmdList.add("" + nThreads);
    }
    cmdList.add("-i");
    cmdList.add(filename);
    // Make sure FFmpeg doesn't try to encode embedded images into the stream
    cmdList.add("-vn");
    // Encoder threads
    if (nThreads > 0) {
        cmdList.add("-threads");
        cmdList.add("" + nThreads);
    }
    if (params.timeend > 0) {
        cmdList.add("-t");
        cmdList.add("" + params.timeend);
    }
    if (params.mediaRenderer.isTranscodeToMP3()) {
        cmdList.add("-f");
        cmdList.add("mp3");
        cmdList.add("-ab");
        cmdList.add("320000");
    } else if (params.mediaRenderer.isTranscodeToWAV()) {
        cmdList.add("-f");
        cmdList.add("wav");
    } else {
        // default: LPCM
        cmdList.add("-f");
        cmdList.add("s16be");
    }
    if (configuration.isAudioResample()) {
        if (params.mediaRenderer.isTranscodeAudioTo441()) {
            cmdList.add("-ar");
            cmdList.add("44100");
            cmdList.add("-ac");
            cmdList.add("2");
        } else {
            cmdList.add("-ar");
            cmdList.add("48000");
            cmdList.add("-ac");
            cmdList.add("2");
        }
    }
    cmdList.add("pipe:");
    String[] cmdArray = new String[cmdList.size()];
    cmdList.toArray(cmdArray);
    cmdArray = finalizeTranscoderArgs(filename, dlna, media, params, cmdArray);
    ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, params);
    pw.runInNewThread();
    configuration = prev;
    return pw;
}
Also used : PmsConfiguration(net.pms.configuration.PmsConfiguration) ArrayList(java.util.ArrayList) ProcessWrapperImpl(net.pms.io.ProcessWrapperImpl)

Example 15 with PmsConfiguration

use of net.pms.configuration.PmsConfiguration in project UniversalMediaServer by UniversalMediaServer.

the class VLCVideo 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;
    final String filename = dlna.getFileName();
    boolean isWindows = Platform.isWindows();
    setAudioAndSubs(filename, media, params);
    // Make sure we can play this
    CodecConfig config = genConfig(params.mediaRenderer);
    PipeProcess tsPipe = new PipeProcess("VLC" + System.currentTimeMillis() + "." + config.container);
    ProcessWrapper pipe_process = tsPipe.getPipeProcess();
    // XXX 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 start this as early as possible
    pipe_process.runInNewThread();
    tsPipe.deleteLater();
    params.input_pipes[0] = tsPipe;
    params.minBufferSize = params.minFileSize;
    params.secondread_minsize = 100000;
    List<String> cmdList = new ArrayList<>();
    cmdList.add(executable());
    cmdList.add("-I");
    cmdList.add("dummy");
    /**
     * Disable hardware acceleration which is enabled by default,
     * but for hardware acceleration, user must enable it in "VLC Preferences",
     * until they release documentation for new functionalities introduced in 2.1.4+
     */
    if (registry.getVlcVersion() != null) {
        String vlcVersion = registry.getVlcVersion();
        Version currentVersion = new Version(vlcVersion);
        Version requiredVersion = new Version("2.1.4");
        if (currentVersion.compareTo(requiredVersion) > 0) {
            if (!configuration.isGPUAcceleration()) {
                cmdList.add("--avcodec-hw=disabled");
                LOGGER.trace("Disabled VLC's hardware acceleration.");
            }
        } else if (!configuration.isGPUAcceleration()) {
            LOGGER.trace("Version " + vlcVersion + " of VLC is too low to handle the way we disable hardware acceleration.");
        }
    }
    // Useful for the more esoteric codecs people use
    if (experimentalCodecs.isSelected()) {
        cmdList.add("--sout-avcodec-strict=-2");
    }
    // Stop the DOS box from appearing on windows
    if (isWindows) {
        cmdList.add("--dummy-quiet");
    }
    // File needs to be given before sout, otherwise vlc complains
    cmdList.add(filename);
    String disableSuffix = "track=-1";
    // Handle audio language
    if (params.aid != null) {
        // User specified language at the client, acknowledge it
        if (params.aid.getLang() == null || params.aid.getLang().equals("und")) {
            // VLC doesn't understand "und", so try to get audio track by ID
            cmdList.add("--audio-track=" + params.aid.getId());
        } else {
            cmdList.add("--audio-language=" + params.aid.getLang());
        }
    } else {
        // Not specified, use language from GUI
        // FIXME: VLC does not understand "loc" or "und".
        cmdList.add("--audio-language=" + configuration.getAudioLanguages());
    }
    // Handle subtitle language
    if (params.sid != null) {
        // User specified language at the client, acknowledge it
        if (params.sid.isExternal() && !params.sid.isStreamable() && !params.mediaRenderer.streamSubsForTranscodedVideo()) {
            String externalSubtitlesFileName;
            // External subtitle file
            if (params.sid.isExternalFileUtf16()) {
                try {
                    // Convert UTF-16 -> UTF-8
                    File convertedSubtitles = new File(configuration.getTempFolder(), "utf8_" + params.sid.getExternalFile().getName());
                    FileUtil.convertFileFromUtf16ToUtf8(params.sid.getExternalFile(), convertedSubtitles);
                    externalSubtitlesFileName = ProcessUtil.getShortFileNameIfWideChars(convertedSubtitles.getAbsolutePath());
                } catch (IOException e) {
                    LOGGER.debug("Error converting file from UTF-16 to UTF-8", e);
                    externalSubtitlesFileName = ProcessUtil.getShortFileNameIfWideChars(params.sid.getExternalFile().getAbsolutePath());
                }
            } else {
                externalSubtitlesFileName = ProcessUtil.getShortFileNameIfWideChars(params.sid.getExternalFile().getAbsolutePath());
            }
            if (externalSubtitlesFileName != null) {
                cmdList.add("--sub-file");
                cmdList.add(externalSubtitlesFileName);
            }
        } else if (params.sid.getLang() != null && !params.sid.getLang().equals("und")) {
            // Load by ID (better)
            cmdList.add("--sub-track=" + params.sid.getId());
        } else {
            // VLC doesn't understand "und", but does understand a nonexistent track
            cmdList.add("--sub-" + disableSuffix);
        }
    } else {
        cmdList.add("--sub-" + disableSuffix);
    }
    // x264 options
    if (videoRemux) {
        cmdList.add("--sout-x264-preset");
        cmdList.add("superfast");
        /**
         * This option prevents VLC from speeding up transcoding by disabling certain
         * codec optimizations if the CPU is struggling to keep up.
         * It is already disabled by default so there is no reason to specify that here,
         * plus the option doesn't work on versions of VLC from 2.0.7 to 2.1.5.
         */
        // cmdList.add("--no-sout-avcodec-hurry-up");
        cmdList.addAll(getVideoBitrateOptions(dlna, media, params));
    }
    // Skip forward if necessary
    if (params.timeseek != 0) {
        cmdList.add("--start-time");
        cmdList.add(String.valueOf(params.timeseek));
    }
    // Generate encoding args
    String separator = "";
    StringBuilder encodingArgsBuilder = new StringBuilder();
    for (Map.Entry<String, Object> curEntry : getEncodingArgs(config, params).entrySet()) {
        encodingArgsBuilder.append(separator);
        encodingArgsBuilder.append(curEntry.getKey());
        if (curEntry.getValue() != null) {
            encodingArgsBuilder.append('=');
            encodingArgsBuilder.append(curEntry.getValue());
        }
        separator = ",";
    }
    // Add our transcode options
    String transcodeSpec = String.format("#transcode{%s}:standard{access=file,mux=%s,dst='%s%s'}", encodingArgsBuilder.toString(), config.container, (isWindows ? "\\\\" : ""), tsPipe.getInputPipe());
    cmdList.add("--sout");
    cmdList.add(transcodeSpec);
    // Force VLC to exit when finished
    cmdList.add("vlc://quit");
    // Pass to process wrapper
    String[] cmdArray = new String[cmdList.size()];
    cmdList.toArray(cmdArray);
    cmdArray = finalizeTranscoderArgs(filename, dlna, media, params, cmdArray);
    LOGGER.trace("Finalized args: " + StringUtils.join(cmdArray, " "));
    ProcessWrapperImpl pw = new ProcessWrapperImpl(cmdArray, params);
    pw.attachProcess(pipe_process);
    // TODO: Why is this here?
    try {
        Thread.sleep(150);
    } catch (InterruptedException e) {
    }
    pw.runInNewThread();
    configuration = prev;
    return pw;
}
Also used : IOException(java.io.IOException) PmsConfiguration(net.pms.configuration.PmsConfiguration) File(java.io.File)

Aggregations

PmsConfiguration (net.pms.configuration.PmsConfiguration)32 File (java.io.File)7 ProcessWrapperImpl (net.pms.io.ProcessWrapperImpl)7 ArrayList (java.util.ArrayList)6 OutputParams (net.pms.io.OutputParams)5 IOException (java.io.IOException)4 ProcessWrapper (net.pms.io.ProcessWrapper)4 LoggerContext (ch.qos.logback.classic.LoggerContext)3 SyslogAppender (ch.qos.logback.classic.net.SyslogAppender)3 ILoggingEvent (ch.qos.logback.classic.spi.ILoggingEvent)3 Appender (ch.qos.logback.core.Appender)3 ConsoleAppender (ch.qos.logback.core.ConsoleAppender)3 FileAppender (ch.qos.logback.core.FileAppender)3 OutputStreamAppender (ch.qos.logback.core.OutputStreamAppender)3 RendererConfiguration (net.pms.configuration.RendererConfiguration)3 PipeProcess (net.pms.io.PipeProcess)3 ThresholdFilter (ch.qos.logback.classic.filter.ThresholdFilter)2 PrintWriter (java.io.PrintWriter)2 StringTokenizer (java.util.StringTokenizer)2 DLNAMediaAudio (net.pms.dlna.DLNAMediaAudio)2