List of usage examples for java.util List listIterator
ListIterator<E> listIterator();
From source file:org.cruk.genologics.api.cache.GenologicsAPICache.java
/** * Join point for the {@code GenologicsAPI.loadAll} method. * Examines the cache for objects already loaded and only fetches those * that are not already seen (or, for stateful objects, those whose requested * state is later than that in the cache). * * @param <E> The type of LIMS entity referred to. * @param pjp The join point object./*from w ww. ja v a2 s. c o m*/ * * @return The list of entities retrieved. * * @throws Throwable if there is an error. * * @see GenologicsAPI#loadAll(Collection) */ public <E extends Locatable> List<E> loadAll(ProceedingJoinPoint pjp) throws Throwable { @SuppressWarnings("unchecked") Collection<LimsLink<E>> links = (Collection<LimsLink<E>>) pjp.getArgs()[0]; List<E> results = new ArrayList<E>(links == null ? 0 : links.size()); if (links != null && !links.isEmpty()) { Ehcache cache = null; List<LimsLink<E>> toFetch = new ArrayList<LimsLink<E>>(links.size()); List<E> alreadyCached = new ArrayList<E>(links.size()); Boolean cacheable = null; String className = null; Boolean stateful = null; CacheStatefulBehaviour callBehaviour = behaviourOverride.get(); if (callBehaviour == null) { callBehaviour = behaviour; } behaviourLock.lock(); try { Iterator<LimsLink<E>> linkIterator = links.iterator(); // Loop through the links requested and accumulate two lists of links: // those that are not in the cache and need to be fetched and those that // have already been fetched. While doing this, assemble in "results" those // entities already in the cache that don't need to be fetch. This list will // have nulls inserted where the entity needs to be fetched. while (linkIterator.hasNext()) { LimsLink<E> link = linkIterator.next(); if (link == null) { throw new IllegalArgumentException("link contains a null"); } if (link.getUri() == null) { throw new IllegalArgumentException("A link in the collection has no URI set."); } if (className == null) { className = ClassUtils.getShortClassName(link.getEntityClass()); cacheable = isCacheable(link.getEntityClass()); stateful = isStateful(link.getEntityClass()); } E entity = null; if (!cacheable) { // Fetch always. toFetch.add(link); } else { if (cache == null) { cache = getCache(link.getEntityClass()); } String key = keyFromLocatable(link); Element wrapper = cache.get(key); if (wrapper == null) { toFetch.add(link); } else { long version = versionFromLocatable(link); switch (callBehaviour) { case ANY: entity = getFromWrapper(wrapper); alreadyCached.add(entity); break; case LATEST: if (version != NO_STATE_VALUE && version > wrapper.getVersion()) { toFetch.add(link); } else { entity = getFromWrapper(wrapper); alreadyCached.add(entity); } break; case EXACT: if (version != NO_STATE_VALUE && version != wrapper.getVersion()) { toFetch.add(link); } else { entity = getFromWrapper(wrapper); alreadyCached.add(entity); } break; } } } results.add(entity); } } finally { behaviourLock.unlock(); } if (logger.isWarnEnabled()) { if (cache.getCacheConfiguration().getMaxEntriesLocalHeap() < links.size()) { logger.warn( "{} {}s are requested, but the cache will only hold {}. Repeated fetches of this collection will always call through to the API.", links.size(), className, cache.getCacheConfiguration().getMaxEntriesLocalHeap()); } } if (logger.isDebugEnabled()) { if (alreadyCached.size() == links.size()) { logger.debug("All {} {}s requested are already in the cache.", links.size(), className); } else { logger.debug("Have {} {}s in the cache; {} to retrieve.", alreadyCached.size(), className, toFetch.size()); } } // If there is anything to fetch, perform the call to the API then // fill in the nulls in the "results" list from the entities returned // from the API. // The end result is that newly fetched items are put into the cache // and "results" is a fully populated list. if (!toFetch.isEmpty()) { assert cacheable != null : "No cacheable flag found"; assert stateful != null : "No stateful flag found"; Object[] args = { toFetch }; @SuppressWarnings("unchecked") List<E> fetched = (List<E>) pjp.proceed(args); ListIterator<E> resultIterator = results.listIterator(); ListIterator<E> fetchIterator = fetched.listIterator(); while (resultIterator.hasNext()) { E entity = resultIterator.next(); if (entity == null) { assert fetchIterator.hasNext() : "Run out of items in the fetched list."; entity = fetchIterator.next(); resultIterator.set(entity); if (cacheable) { if (!stateful) { // Entities without state will only have been fetched because they // were not in the cache. These should just be added. cache.put(createCacheElement(entity)); } else { // Stateful entities may already be in the cache but may have been // fetched because the requested version is newer or of a different // state. Some care needs to be taken to update its cached version // depending on how the cache normally behaves. String key = keyFromLocatable(entity); Element wrapper = cache.get(key); if (wrapper == null) { // Not already cached, so simply add this entity whatever // its state. cache.put(createCacheElement(entity)); } else { // As we have a stateful entity, there may be cause // to replace the object in the cache depending on how the // cache normally behaves. Typically this will be replacing the // existing with a newer version or replacing for a difference. // When we don't care about versions, the one already in the cache // can remain. long version = versionFromLocatable(entity); switch (behaviour) { case ANY: break; case LATEST: if (version > wrapper.getVersion()) { cache.put(createCacheElement(entity)); } break; case EXACT: if (version != wrapper.getVersion()) { cache.put(createCacheElement(entity)); } break; } } } } } } assert !fetchIterator.hasNext() : "Have further items fetched after populating results list."; } } return results; }
From source file:net.sf.jasperreports.engine.util.JEditorPaneHtmlMarkupProcessor.java
@Override public String convert(String srcText) { JEditorPane editorPane = new JEditorPane("text/html", srcText); editorPane.setEditable(false);// www. j a va2 s. c o m List<Element> elements = new ArrayList<Element>(); Document document = editorPane.getDocument(); Element root = document.getDefaultRootElement(); if (root != null) { addElements(elements, root); } int startOffset = 0; int endOffset = 0; int crtOffset = 0; String chunk = null; JRPrintHyperlink hyperlink = null; Element element = null; Element parent = null; boolean bodyOccurred = false; int[] orderedListIndex = new int[elements.size()]; String whitespace = " "; String[] whitespaces = new String[elements.size()]; for (int i = 0; i < elements.size(); i++) { whitespaces[i] = ""; } StringBuilder text = new StringBuilder(); List<JRStyledText.Run> styleRuns = new ArrayList<>(); for (int i = 0; i < elements.size(); i++) { if (bodyOccurred && chunk != null) { text.append(chunk); Map<Attribute, Object> styleAttributes = getAttributes(element.getAttributes()); if (hyperlink != null) { styleAttributes.put(JRTextAttribute.HYPERLINK, hyperlink); hyperlink = null; } if (!styleAttributes.isEmpty()) { styleRuns.add( new JRStyledText.Run(styleAttributes, startOffset + crtOffset, endOffset + crtOffset)); } } chunk = null; element = elements.get(i); parent = element.getParentElement(); startOffset = element.getStartOffset(); endOffset = element.getEndOffset(); AttributeSet attrs = element.getAttributes(); Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute); Object object = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute); if (object instanceof HTML.Tag) { HTML.Tag htmlTag = (HTML.Tag) object; if (htmlTag == Tag.BODY) { bodyOccurred = true; crtOffset = -startOffset; } else if (htmlTag == Tag.BR) { chunk = "\n"; } else if (htmlTag == Tag.OL) { orderedListIndex[i] = 0; String parentName = parent.getName().toLowerCase(); whitespaces[i] = whitespaces[elements.indexOf(parent)] + whitespace; if (parentName.equals("li")) { chunk = ""; } else { chunk = "\n"; ++crtOffset; } } else if (htmlTag == Tag.UL) { whitespaces[i] = whitespaces[elements.indexOf(parent)] + whitespace; String parentName = parent.getName().toLowerCase(); if (parentName.equals("li")) { chunk = ""; } else { chunk = "\n"; ++crtOffset; } } else if (htmlTag == Tag.LI) { whitespaces[i] = whitespaces[elements.indexOf(parent)]; if (element.getElement(0) != null && (element.getElement(0).getName().toLowerCase().equals("ol") || element.getElement(0).getName().toLowerCase().equals("ul"))) { chunk = ""; } else if (parent.getName().equals("ol")) { int index = elements.indexOf(parent); Object type = parent.getAttributes().getAttribute(HTML.Attribute.TYPE); Object startObject = parent.getAttributes().getAttribute(HTML.Attribute.START); int start = startObject == null ? 0 : Math.max(0, Integer.valueOf(startObject.toString()) - 1); String suffix = ""; ++orderedListIndex[index]; if (type != null) { switch (((String) type).charAt(0)) { case 'A': suffix = getOLBulletChars(orderedListIndex[index] + start, true); break; case 'a': suffix = getOLBulletChars(orderedListIndex[index] + start, false); break; case 'I': suffix = JRStringUtil.getRomanNumeral(orderedListIndex[index] + start, true); break; case 'i': suffix = JRStringUtil.getRomanNumeral(orderedListIndex[index] + start, false); break; case '1': default: suffix = String.valueOf(orderedListIndex[index] + start); break; } } else { suffix += orderedListIndex[index] + start; } chunk = whitespaces[index] + suffix + DEFAULT_BULLET_SEPARATOR + " "; } else { chunk = whitespaces[elements.indexOf(parent)] + DEFAULT_BULLET_CHARACTER + " "; } crtOffset += chunk.length(); } else if (element instanceof LeafElement) { if (element instanceof RunElement) { RunElement runElement = (RunElement) element; AttributeSet attrSet = (AttributeSet) runElement.getAttribute(Tag.A); if (attrSet != null) { hyperlink = new JRBasePrintHyperlink(); hyperlink.setHyperlinkType(HyperlinkTypeEnum.REFERENCE); hyperlink.setHyperlinkReference((String) attrSet.getAttribute(HTML.Attribute.HREF)); hyperlink.setLinkTarget((String) attrSet.getAttribute(HTML.Attribute.TARGET)); } } try { chunk = document.getText(startOffset, endOffset - startOffset); } catch (BadLocationException e) { if (log.isDebugEnabled()) { log.debug("Error converting markup.", e); } } } } } if (chunk != null) { if (!"\n".equals(chunk)) { text.append(chunk); Map<Attribute, Object> styleAttributes = getAttributes(element.getAttributes()); if (hyperlink != null) { styleAttributes.put(JRTextAttribute.HYPERLINK, hyperlink); hyperlink = null; } if (!styleAttributes.isEmpty()) { styleRuns.add( new JRStyledText.Run(styleAttributes, startOffset + crtOffset, endOffset + crtOffset)); } } else { //final newline, not appending //check if there's any style run that would have covered it, that can happen if there's a <li> tag with style int length = text.length(); for (ListIterator<JRStyledText.Run> it = styleRuns.listIterator(); it.hasNext();) { JRStyledText.Run run = it.next(); //only looking at runs that end at the position where the newline should have been //we don't want to hide bugs in which runs that span after the text length are created if (run.endIndex == length + 1) { if (run.startIndex < run.endIndex - 1) { it.set(new JRStyledText.Run(run.attributes, run.startIndex, run.endIndex - 1)); } else { it.remove(); } } } } } JRStyledText styledText = new JRStyledText(null, text.toString()); for (JRStyledText.Run run : styleRuns) { styledText.addRun(run); } styledText.setGlobalAttributes(new HashMap<Attribute, Object>()); return JRStyledTextParser.getInstance().write(styledText); }
From source file:net.pms.encoders.MEncoderVideo.java
@Override public ProcessWrapper launchTranscode(DLNAResource dlna, DLNAMediaInfo media, OutputParams params) throws IOException { params.manageFastStart();//from w ww. j a va2 s .c o m boolean avisynth = avisynth(); final String filename = dlna.getSystemName(); setAudioAndSubs(filename, media, params, configuration); String externalSubtitlesFileName = null; if (params.sid != null && params.sid.isExternal()) { if (params.sid.isExternalFileUtf16()) { // convert UTF-16 -> UTF-8 File convertedSubtitles = new File(PMS.getConfiguration().getTempFolder(), "utf8_" + params.sid.getExternalFile().getName()); FileUtil.convertFileFromUtf16ToUtf8(params.sid.getExternalFile(), convertedSubtitles); externalSubtitlesFileName = ProcessUtil .getShortFileNameIfWideChars(convertedSubtitles.getAbsolutePath()); } else { externalSubtitlesFileName = ProcessUtil .getShortFileNameIfWideChars(params.sid.getExternalFile().getAbsolutePath()); } } InputFile newInput = new InputFile(); newInput.setFilename(filename); newInput.setPush(params.stdin); dvd = false; if (media != null && media.getDvdtrack() > 0) { dvd = true; } ovccopy = false; pcm = false; ac3Remux = false; dtsRemux = false; wmv = false; int intOCW = 0; int intOCH = 0; try { intOCW = Integer.parseInt(configuration.getMencoderOverscanCompensationWidth()); } catch (NumberFormatException e) { logger.error("Cannot parse configured MEncoder overscan compensation width: \"{}\"", configuration.getMencoderOverscanCompensationWidth()); } try { intOCH = Integer.parseInt(configuration.getMencoderOverscanCompensationHeight()); } catch (NumberFormatException e) { logger.error("Cannot parse configured MEncoder overscan compensation height: \"{}\"", configuration.getMencoderOverscanCompensationHeight()); } if (params.sid == null && dvd && configuration.isMencoderRemuxMPEG2() && params.mediaRenderer.isMpeg2Supported()) { String expertOptions[] = getSpecificCodecOptions(configuration.getMencoderCodecSpecificConfig(), media, params, filename, externalSubtitlesFileName, configuration.isMencoderIntelligentSync(), false); boolean nomux = false; for (String s : expertOptions) { if (s.equals("-nomux")) { nomux = true; } } if (!nomux) { ovccopy = true; } } String vcodec = "mpeg2video"; if (params.mediaRenderer.isTranscodeToWMV()) { wmv = true; vcodec = "wmv2"; // http://wiki.megaframe.org/wiki/Ubuntu_XBOX_360#MEncoder not usable in streaming } mpegts = params.mediaRenderer.isTranscodeToMPEGTSAC3(); /* 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.aid != null // && (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; final boolean isTSMuxerVideoEngineEnabled = PMS.getConfiguration().getEnginesAsList() .contains(TsMuxeRVideo.ID); final boolean mencoderAC3RemuxAudioDelayBug = (params.aid != null) && (params.aid.getAudioProperties().getAudioDelay() != 0) && (params.timeseek == 0); if (!mencoderAC3RemuxAudioDelayBug && configuration.isAudioRemuxAC3() && params.aid != null && params.aid.isAC3() && !ps3_and_stereo_and_384_kbits && !avisynth() && params.mediaRenderer.isTranscodeToAC3()) { // AC3 remux takes priority ac3Remux = true; } else { // now check for DTS remux and LPCM streaming dtsRemux = isTSMuxerVideoEngineEnabled && configuration.isAudioEmbedDtsInPcm() && (!dvd || configuration.isMencoderRemuxMPEG2()) && params.aid != null && params.aid.isDTS() && !avisynth() && params.mediaRenderer.isDTSPlayable(); pcm = isTSMuxerVideoEngineEnabled && configuration.isAudioUsePCM() && (!dvd || configuration.isMencoderRemuxMPEG2()) // disable LPCM transcoding for MP4 container with non-H264 video as workaround for mencoder's A/V sync bug && !(media.getContainer().equals("mp4") && !media.getCodecV().equals("h264")) && params.aid != null && ((params.aid.isDTS() && params.aid.getAudioProperties().getNumberOfChannels() <= 6) || // disable 7.1 DTS-HD => LPCM because of channels mapping bug params.aid.isLossless() || params.aid.isTrueHD() || (!configuration.isMencoderUsePcmForHQAudioOnly() && (params.aid.isAC3() || params.aid.isMP3() || params.aid.isAAC() || params.aid.isVorbis() || // disable WMA to LPCM transcoding because of mencoder's channel mapping bug // (see CodecUtil.getMixerOutput) // params.aid.isWMA() || params.aid.isMpegAudio()))) && params.mediaRenderer.isLPCMPlayable(); } if (dtsRemux || pcm) { params.losslessaudio = true; params.forceFps = media.getValidFps(false); } // mpeg2 remux still buggy with mencoder :\ // TODO when we can still use it? ovccopy = false; if (pcm && avisynth()) { params.avidemux = true; } int channels; if (ac3Remux) { channels = params.aid.getAudioProperties().getNumberOfChannels(); // ac3 remux } else if (dtsRemux || wmv) { channels = 2; } else if (pcm) { channels = params.aid.getAudioProperties().getNumberOfChannels(); } else { channels = configuration.getAudioChannelCount(); // 5.1 max for ac3 encoding } logger.trace("channels=" + channels); String add = ""; String rendererMencoderOptions = params.mediaRenderer.getCustomMencoderOptions(); // default: empty string String globalMencoderOptions = configuration.getMencoderCustomOptions(); // default: empty string if (params.mediaRenderer.isPadVideoWithBlackBordersTo169AR()) { rendererMencoderOptions += " -vf softskip,expand=::::1:16/9:4"; } String combinedCustomOptions = defaultString(globalMencoderOptions) + " " + defaultString(rendererMencoderOptions); if (!combinedCustomOptions.contains("-lavdopts")) { add = " -lavdopts debug=0"; } if (isNotBlank(rendererMencoderOptions)) { // don't use the renderer-specific options if they break DVD streaming // XXX we should weed out the unused/unwanted settings and keep the rest // (see sanitizeArgs()) rather than ignoring the options entirely if (dvd && rendererMencoderOptions.contains("expand=")) { logger.warn("renderer MEncoder options are incompatible with DVD streaming; ignoring: " + rendererMencoderOptions); rendererMencoderOptions = null; } } StringTokenizer st = new StringTokenizer( "-channels " + channels + (isNotBlank(globalMencoderOptions) ? " " + globalMencoderOptions : "") + (isNotBlank(rendererMencoderOptions) ? " " + rendererMencoderOptions : "") + add, " "); // XXX why does this field (which is used to populate the array returned by args(), // called below) store the renderer-specific (i.e. not global) MEncoder options? overriddenMainArgs = new String[st.countTokens()]; { int nThreads = (dvd || filename.toLowerCase().endsWith("dvr-ms")) ? 1 : configuration.getMencoderMaxThreads(); boolean handleToken = false; int i = 0; while (st.hasMoreTokens()) { String token = st.nextToken().trim(); if (handleToken) { token += ":threads=" + nThreads; if (configuration.getSkipLoopFilterEnabled() && !avisynth()) { token += ":skiploopfilter=all"; } handleToken = false; } if (token.toLowerCase().contains("lavdopts")) { handleToken = true; } overriddenMainArgs[i++] = token; } } if (configuration.getMPEG2MainSettings() != null) { String mpeg2Options = configuration.getMPEG2MainSettings(); String mpeg2OptionsRenderer = params.mediaRenderer.getCustomMEncoderMPEG2Options(); // Renderer settings take priority over user settings if (isNotBlank(mpeg2OptionsRenderer)) { mpeg2Options = mpeg2OptionsRenderer; } else { // Remove comment from the value if (mpeg2Options.contains("/*")) { mpeg2Options = mpeg2Options.substring(mpeg2Options.indexOf("/*")); } // Find out the maximum bandwidth we are supposed to use int defaultMaxBitrates[] = getVideoBitrateConfig(configuration.getMaximumBitrate()); int rendererMaxBitrates[] = new int[2]; if (params.mediaRenderer.getMaxVideoBitrate() != null) { rendererMaxBitrates = getVideoBitrateConfig(params.mediaRenderer.getMaxVideoBitrate()); } if ((rendererMaxBitrates[0] > 0) && (rendererMaxBitrates[0] < defaultMaxBitrates[0])) { defaultMaxBitrates = rendererMaxBitrates; } int maximumBitrate = defaultMaxBitrates[0]; // Determine a good quality setting based on video attributes if (mpeg2Options.contains("Automatic")) { mpeg2Options = "keyint=5:vqscale=1:vqmin=2:vqmax=3"; // It has been reported that non-PS3 renderers prefer keyint 5 but prefer it for PS3 because it lowers the average bitrate if (params.mediaRenderer.isPS3()) { mpeg2Options = "keyint=25:vqscale=1:vqmin=2:vqmax=3"; } if (mpeg2Options.contains("Wireless") || maximumBitrate < 70) { // Lower quality for 720p+ content if (media.getWidth() > 1280) { mpeg2Options = "keyint=25:vqmax=7:vqmin=2"; } else if (media.getWidth() > 720) { mpeg2Options = "keyint=25:vqmax=5:vqmin=2"; } } } } // Ditlew - WDTV Live (+ other byte asking clients), CBR. This probably ought to be placed in addMaximumBitrateConstraints(..) int cbr_bitrate = params.mediaRenderer.getCBRVideoBitrate(); String cbr_settings = (cbr_bitrate > 0) ? ":vrc_buf_size=5000:vrc_minrate=" + cbr_bitrate + ":vrc_maxrate=" + cbr_bitrate + ":vbitrate=" + ((cbr_bitrate > 16000) ? cbr_bitrate * 1000 : cbr_bitrate) : ""; String encodeSettings = "-lavcopts autoaspect=1:vcodec=" + vcodec + (wmv && !params.mediaRenderer.isXBOX() ? ":acodec=wmav2:abitrate=448" : (cbr_settings + ":acodec=" + (configuration.isMencoderAc3Fixed() ? "ac3_fixed" : "ac3") + ":abitrate=" + CodecUtil.getAC3Bitrate(configuration, params.aid))) + ":threads=" + (wmv && !params.mediaRenderer.isXBOX() ? 1 : configuration.getMencoderMaxThreads()) + ("".equals(mpeg2Options) ? "" : ":" + mpeg2Options); String audioType = "ac3"; if (dtsRemux) { audioType = "dts"; } else if (pcm) { audioType = "pcm"; } encodeSettings = addMaximumBitrateConstraints(encodeSettings, media, mpeg2Options, params.mediaRenderer, audioType); st = new StringTokenizer(encodeSettings, " "); { int i = overriddenMainArgs.length; // Old length overriddenMainArgs = Arrays.copyOf(overriddenMainArgs, overriddenMainArgs.length + st.countTokens()); while (st.hasMoreTokens()) { overriddenMainArgs[i++] = st.nextToken(); } } } boolean foundNoassParam = false; if (media != null) { String expertOptions[] = getSpecificCodecOptions(configuration.getMencoderCodecSpecificConfig(), media, params, filename, externalSubtitlesFileName, configuration.isMencoderIntelligentSync(), false); for (String s : expertOptions) { if (s.equals("-noass")) { foundNoassParam = true; } } } StringBuilder sb = new StringBuilder(); // Set subtitles options if (!configuration.isDisableSubtitles() && !avisynth() && params.sid != null) { int subtitleMargin = 0; int userMargin = 0; // Use ASS flag (and therefore ASS font styles) for all subtitled files except vobsub, PGS and dvd boolean apply_ass_styling = params.sid.getType() != SubtitleType.VOBSUB && params.sid.getType() != SubtitleType.PGS && configuration.isMencoderAss() && // GUI: enable subtitles formating !foundNoassParam && // GUI: codec specific options !dvd; if (apply_ass_styling) { sb.append("-ass "); // GUI: Override ASS subtitles style if requested (always for SRT and TX3G subtitles) boolean override_ass_style = !configuration.isMencoderAssDefaultStyle() || params.sid.getType() == SubtitleType.SUBRIP || params.sid.getType() == SubtitleType.TX3G; if (override_ass_style) { String assSubColor = "ffffff00"; if (configuration.getSubsColor() != 0) { assSubColor = Integer.toHexString(configuration.getSubsColor()); if (assSubColor.length() > 2) { assSubColor = assSubColor.substring(2) + "00"; } } sb.append("-ass-color ").append(assSubColor) .append(" -ass-border-color 00000000 -ass-font-scale ") .append(configuration.getAssScale()); // set subtitles font if (configuration.getFont() != null && configuration.getFont().length() > 0) { // set font with -font option, workaround for // https://github.com/Happy-Neko/ps3mediaserver/commit/52e62203ea12c40628de1869882994ce1065446a#commitcomment-990156 bug sb.append(" -font ").append(configuration.getFont()).append(" "); sb.append(" -ass-force-style FontName=").append(configuration.getFont()).append(","); } else { String font = CodecUtil.getDefaultFontPath(); if (isNotBlank(font)) { // Variable "font" contains a font path instead of a font name. // Does "-ass-force-style" support font paths? In tests on OS X // the font path is ignored (Outline, Shadow and MarginV are // used, though) and the "-font" definition is used instead. // See: https://github.com/ps3mediaserver/ps3mediaserver/pull/14 sb.append(" -font ").append(font).append(" "); sb.append(" -ass-force-style FontName=").append(font).append(","); } else { sb.append(" -font Arial "); sb.append(" -ass-force-style FontName=Arial,"); } } // Add to the subtitle margin if overscan compensation is being used // This keeps the subtitle text inside the frame instead of in the border if (intOCH > 0) { subtitleMargin = (media.getHeight() / 100) * intOCH; } sb.append("Outline=").append(configuration.getAssOutline()).append(",Shadow=") .append(configuration.getAssShadow()); try { userMargin = Integer.parseInt(configuration.getAssMargin()); } catch (NumberFormatException n) { logger.debug("Could not parse SSA margin from \"" + configuration.getAssMargin() + "\""); } subtitleMargin = subtitleMargin + userMargin; sb.append(",MarginV=").append(subtitleMargin).append(" "); } else if (intOCH > 0) { sb.append("-ass-force-style MarginV=").append(subtitleMargin).append(" "); } // MEncoder is not compiled with fontconfig on Mac OS X, therefore // use of the "-ass" option also requires the "-font" option. if (Platform.isMac() && sb.toString().indexOf(" -font ") < 0) { String font = CodecUtil.getDefaultFontPath(); if (isNotBlank(font)) { sb.append("-font ").append(font).append(" "); } } // Workaround for MPlayer #2041, remove when that bug is fixed if (!params.sid.isEmbedded()) { sb.append("-noflip-hebrew "); } // use PLAINTEXT formating } else { // set subtitles font if (configuration.getFont() != null && configuration.getFont().length() > 0) { sb.append(" -font ").append(configuration.getFont()).append(" "); } else { String font = CodecUtil.getDefaultFontPath(); if (isNotBlank(font)) { sb.append(" -font ").append(font).append(" "); } } sb.append(" -subfont-text-scale ").append(configuration.getMencoderNoAssScale()); sb.append(" -subfont-outline ").append(configuration.getMencoderNoAssOutline()); sb.append(" -subfont-blur ").append(configuration.getMencoderNoAssBlur()); // Add to the subtitle margin if overscan compensation is being used // This keeps the subtitle text inside the frame instead of in the border if (intOCH > 0) { subtitleMargin = intOCH; } try { userMargin = Integer.parseInt(configuration.getMencoderNoAssSubPos()); } catch (NumberFormatException n) { logger.debug("Could not parse subpos from \"" + configuration.getMencoderNoAssSubPos() + "\""); } subtitleMargin = subtitleMargin + userMargin; sb.append(" -subpos ").append(100 - subtitleMargin).append(" "); } // Common subtitle options // MEncoder on Mac OS X is compiled without fontconfig support. // Appending the flag will break execution, so skip it on Mac OS X. if (!Platform.isMac()) { // Use fontconfig if enabled sb.append("-").append(configuration.isMencoderFontConfig() ? "" : "no").append("fontconfig "); } // Apply DVD/VOBSUB subtitle quality if (params.sid.getType() == SubtitleType.VOBSUB && configuration.getMencoderVobsubSubtitleQuality() != null) { String subtitleQuality = configuration.getMencoderVobsubSubtitleQuality(); sb.append("-spuaa ").append(subtitleQuality).append(" "); } // external subtitles file if (params.sid.isExternal()) { if (!params.sid.isExternalFileUtf()) { String subcp = null; // append -subcp option for non UTF external subtitles if (isNotBlank(configuration.getSubtitlesCodepage())) { // manual setting subcp = configuration.getSubtitlesCodepage(); } else if (isNotBlank(SubtitleUtils.getSubCpOptionForMencoder(params.sid))) { // autodetect charset (blank mencoder_subcp config option) subcp = SubtitleUtils.getSubCpOptionForMencoder(params.sid); } if (isNotBlank(subcp)) { sb.append("-subcp ").append(subcp).append(" "); if (configuration.isMencoderSubFribidi()) { sb.append("-fribidi-charset ").append(subcp).append(" "); } } } } } st = new StringTokenizer(sb.toString(), " "); { int i = overriddenMainArgs.length; // old length overriddenMainArgs = Arrays.copyOf(overriddenMainArgs, overriddenMainArgs.length + st.countTokens()); boolean handleToken = false; while (st.hasMoreTokens()) { String s = st.nextToken(); if (handleToken) { s = "-quiet"; handleToken = false; } if ((!configuration.isMencoderAss() || dvd) && s.contains("-ass")) { s = "-quiet"; handleToken = true; } overriddenMainArgs[i++] = s; } } List<String> cmdList = new ArrayList<String>(); cmdList.add(executable()); // timeseek // XXX -ss 0 is is included for parity with the old (cmdArray) code: it may be possible to omit it cmdList.add("-ss"); cmdList.add((params.timeseek > 0) ? "" + params.timeseek : "0"); if (dvd) { cmdList.add("-dvd-device"); } // input filename if (avisynth && !filename.toLowerCase().endsWith(".iso")) { File avsFile = FFmpegAviSynthVideo.getAVSScript(filename, params.sid, params.fromFrame, params.toFrame); cmdList.add(ProcessUtil.getShortFileNameIfWideChars(avsFile.getAbsolutePath())); } else { if (params.stdin != null) { cmdList.add("-"); } else { cmdList.add(filename); } } if (dvd) { cmdList.add("dvd://" + media.getDvdtrack()); } for (String arg : args()) { if (arg.contains("format=mpeg2") && media.getAspect() != null && media.getValidAspect(true) != null) { cmdList.add(arg + ":vaspect=" + media.getValidAspect(true)); } else { cmdList.add(arg); } } if (!dtsRemux && !pcm && !avisynth() && params.aid != null && media.getAudioTracksList().size() > 1) { cmdList.add("-aid"); boolean lavf = false; // TODO Need to add support for LAVF demuxing cmdList.add("" + (lavf ? params.aid.getId() + 1 : params.aid.getId())); } /* * handle subtitles * * try to reconcile the fact that the handling of "Disable subtitles" is spread out * over net.pms.encoders.Player.setAudioAndSubs and here by setting both of MEncoder's "disable * subs" options if any of the internal conditions for disabling subtitles are met. */ if (isDisableSubtitles(params)) { // Ensure that internal subtitles are not automatically loaded // MKV: in some circumstances, MEncoder automatically selects an internal sub unless we explicitly disable (internal) subtitles // http://www.ps3mediaserver.org/forum/viewtopic.php?f=14&t=15891 cmdList.add("-nosub"); // Ensure that external subtitles are not automatically loaded cmdList.add("-noautosub"); } else { // note: isEmbedded() and isExternal() are mutually exclusive if (params.sid.isEmbedded()) { // internal (embedded) subs // Ensure that external subtitles are not automatically loaded cmdList.add("-noautosub"); // Specify which internal subtitle we want cmdList.add("-sid"); cmdList.add("" + params.sid.getId()); } else if (externalSubtitlesFileName != null) { // external subtitles assert params.sid.isExternal(); // confirm the mutual exclusion // Ensure that internal subtitles are not automatically loaded cmdList.add("-nosub"); if (params.sid.getType() == SubtitleType.VOBSUB) { cmdList.add("-vobsub"); cmdList.add(externalSubtitlesFileName.substring(0, externalSubtitlesFileName.length() - 4)); cmdList.add("-slang"); cmdList.add("" + params.sid.getLang()); } else { cmdList.add("-sub"); cmdList.add(externalSubtitlesFileName.replace(",", "\\,")); // Commas in MEncoder separate multiple subtitle files if (params.sid.isExternalFileUtf()) { // append -utf8 option for UTF-8 external subtitles cmdList.add("-utf8"); } } } } // -ofps String validFramerate = (media != null) ? media.getValidFps(true) : null; // optional input framerate: may be null String framerate = (validFramerate != null) ? validFramerate : "24000/1001"; // where a framerate is required, use the input framerate or 24000/1001 String ofps = framerate; // optional -fps or -mc if (configuration.isMencoderForceFps()) { if (!configuration.isFix25FPSAvMismatch()) { cmdList.add("-fps"); cmdList.add(framerate); } else if (validFramerate != null) { // XXX not sure why this "fix" requires the input to have a valid framerate, but that's the logic in the old (cmdArray) code cmdList.add("-mc"); cmdList.add("0.005"); ofps = "25"; } } cmdList.add("-ofps"); cmdList.add(ofps); /* * TODO: Move the following block up with the rest of the * subtitle stuff */ // external subtitles file if (!configuration.isDisableSubtitles() && !avisynth() && params.sid != null && params.sid.isExternal()) { if (params.sid.getType() == SubtitleType.VOBSUB) { cmdList.add("-vobsub"); cmdList.add(externalSubtitlesFileName.substring(0, externalSubtitlesFileName.length() - 4)); cmdList.add("-slang"); cmdList.add("" + params.sid.getLang()); } else { cmdList.add("-sub"); cmdList.add(externalSubtitlesFileName.replace(",", "\\,")); // Commas in MEncoder separate multiple subtitle files if (params.sid.isExternalFileUtf()) { // append -utf8 option for UTF-8 external subtitles cmdList.add("-utf8"); } } } if (filename.toLowerCase().endsWith(".evo")) { cmdList.add("-psprobe"); cmdList.add("10000"); } boolean deinterlace = configuration.isMencoderYadif(); // Check if the media renderer supports this resolution boolean isResolutionTooHighForRenderer = params.mediaRenderer.isVideoRescale() && media != null && ((media.getWidth() > params.mediaRenderer.getMaxVideoWidth()) || (media.getHeight() > params.mediaRenderer.getMaxVideoHeight())); // Video scaler and overscan compensation boolean scaleBool = isResolutionTooHighForRenderer || (configuration.isMencoderScaler() && (configuration.getMencoderScaleX() != 0 || configuration.getMencoderScaleY() != 0)) || (intOCW > 0 || intOCH > 0); if ((deinterlace || scaleBool) && !avisynth()) { StringBuilder vfValueOverscanPrepend = new StringBuilder(); StringBuilder vfValueOverscanMiddle = new StringBuilder(); StringBuilder vfValueVS = new StringBuilder(); StringBuilder vfValueComplete = new StringBuilder(); String deinterlaceComma = ""; int scaleWidth = 0; int scaleHeight = 0; double rendererAspectRatio; // Set defaults if (media != null && media.getWidth() > 0 && media.getHeight() > 0) { scaleWidth = media.getWidth(); scaleHeight = media.getHeight(); } /* * Implement overscan compensation settings * * This feature takes into account aspect ratio, * making it less blunt than the Video Scaler option */ if (intOCW > 0 || intOCH > 0) { int intOCWPixels = (media.getWidth() / 100) * intOCW; int intOCHPixels = (media.getHeight() / 100) * intOCH; scaleWidth = scaleWidth + intOCWPixels; scaleHeight = scaleHeight + intOCHPixels; // See if the video needs to be scaled down if (params.mediaRenderer.isVideoRescale() && ((scaleWidth > params.mediaRenderer.getMaxVideoWidth()) || (scaleHeight > params.mediaRenderer.getMaxVideoHeight()))) { double overscannedAspectRatio = scaleWidth / scaleHeight; rendererAspectRatio = params.mediaRenderer.getMaxVideoWidth() / params.mediaRenderer.getMaxVideoHeight(); if (overscannedAspectRatio > rendererAspectRatio) { // Limit video by width scaleWidth = params.mediaRenderer.getMaxVideoWidth(); scaleHeight = (int) Math .round(params.mediaRenderer.getMaxVideoWidth() / overscannedAspectRatio); } else { // Limit video by height scaleWidth = (int) Math .round(params.mediaRenderer.getMaxVideoHeight() * overscannedAspectRatio); scaleHeight = params.mediaRenderer.getMaxVideoHeight(); } } vfValueOverscanPrepend.append("softskip,expand=-").append(intOCWPixels).append(":-") .append(intOCHPixels); vfValueOverscanMiddle.append(",scale=").append(scaleWidth).append(":").append(scaleHeight); } /* * Video Scaler and renderer-specific resolution-limiter */ if (configuration.isMencoderScaler()) { // Use the manual, user-controlled scaler if (configuration.getMencoderScaleX() != 0) { if (configuration.getMencoderScaleX() <= params.mediaRenderer.getMaxVideoWidth()) { scaleWidth = configuration.getMencoderScaleX(); } else { scaleWidth = params.mediaRenderer.getMaxVideoWidth(); } } if (configuration.getMencoderScaleY() != 0) { if (configuration.getMencoderScaleY() <= params.mediaRenderer.getMaxVideoHeight()) { scaleHeight = configuration.getMencoderScaleY(); } else { scaleHeight = params.mediaRenderer.getMaxVideoHeight(); } } logger.info("Setting video resolution to: " + scaleWidth + "x" + scaleHeight + ", your Video Scaler setting"); vfValueVS.append("scale=").append(scaleWidth).append(":").append(scaleHeight); /* * The video resolution is too big for the renderer so we need to scale it down */ } else if (media != null && media.getWidth() > 0 && media.getHeight() > 0 && (media.getWidth() > params.mediaRenderer.getMaxVideoWidth() || media.getHeight() > params.mediaRenderer.getMaxVideoHeight())) { double videoAspectRatio = (double) media.getWidth() / (double) media.getHeight(); rendererAspectRatio = (double) params.mediaRenderer.getMaxVideoWidth() / (double) params.mediaRenderer.getMaxVideoHeight(); /* * First we deal with some exceptions, then if they are not matched we will * let the renderer limits work. * * This is so, for example, we can still define a maximum resolution of * 1920x1080 in the renderer config file but still support 1920x1088 when * it's needed, otherwise we would either resize 1088 to 1080, meaning the * ugly (unused) bottom 8 pixels would be displayed, or we would limit all * videos to 1088 causing the bottom 8 meaningful pixels to be cut off. */ if (media.getWidth() == 3840 && media.getHeight() == 1080) { // Full-SBS scaleWidth = 1920; scaleHeight = 1080; } else if (media.getWidth() == 1920 && media.getHeight() == 2160) { // Full-OU scaleWidth = 1920; scaleHeight = 1080; } else if (media.getWidth() == 1920 && media.getHeight() == 1088) { // SAT capture scaleWidth = 1920; scaleHeight = 1088; } else { // Passed the exceptions, now we allow the renderer to define the limits if (videoAspectRatio > rendererAspectRatio) { scaleWidth = params.mediaRenderer.getMaxVideoWidth(); scaleHeight = (int) Math.round(params.mediaRenderer.getMaxVideoWidth() / videoAspectRatio); } else { scaleWidth = (int) Math.round(params.mediaRenderer.getMaxVideoHeight() * videoAspectRatio); scaleHeight = params.mediaRenderer.getMaxVideoHeight(); } } logger.info("Setting video resolution to: " + scaleWidth + "x" + scaleHeight + ", the maximum your renderer supports"); vfValueVS.append("scale=").append(scaleWidth).append(":").append(scaleHeight); } // Put the string together taking into account overscan compensation and video scaler if (intOCW > 0 || intOCH > 0) { vfValueComplete.append(vfValueOverscanPrepend).append(vfValueOverscanMiddle).append(",harddup"); logger.info("Setting video resolution to: " + scaleWidth + "x" + scaleHeight + ", to fit your overscan compensation"); } else { vfValueComplete.append(vfValueVS); } if (deinterlace) { deinterlaceComma = ","; } String vfValue = (deinterlace ? "yadif" : "") + (scaleBool ? deinterlaceComma + vfValueComplete : ""); if (isNotBlank(vfValue)) { cmdList.add("-vf"); cmdList.add(vfValue); } } /* * The PS3 and possibly other renderers display videos incorrectly * if the dimensions aren't divisible by 4, so if that is the * case we scale it down to the nearest 4. * This fixes the long-time bug of videos displaying in black and * white with diagonal strips of colour, weird one. * * TODO: Integrate this with the other stuff so that "scale" only * ever appears once in the MEncoder CMD. */ if (media != null && (media.getWidth() % 4 != 0) || media.getHeight() % 4 != 0) { int newWidth; int newHeight; newWidth = (media.getWidth() / 4) * 4; newHeight = (media.getHeight() / 4) * 4; cmdList.add("-vf"); cmdList.add("softskip,expand=" + newWidth + ":" + newHeight); } if (configuration.getMencoderMT() && !avisynth && !dvd && !(startsWith(media.getCodecV(), "mpeg2"))) { cmdList.add("-lavdopts"); cmdList.add("fast"); } boolean disableMc0AndNoskip = false; // Process the options for this file in Transcoding Settings -> Mencoder -> Expert Settings: Codec-specific parameters // TODO this is better handled by a plugin with scripting support and will be removed if (media != null) { String expertOptions[] = getSpecificCodecOptions(configuration.getMencoderCodecSpecificConfig(), media, params, filename, externalSubtitlesFileName, configuration.isMencoderIntelligentSync(), false); // the parameters (expertOptions) are processed in 3 passes // 1) process expertOptions // 2) process cmdList // 3) append expertOptions to cmdList if (expertOptions != null && expertOptions.length > 0) { // remove this option (key) from the cmdList in pass 2. // if the boolean value is true, also remove the option's corresponding value Map<String, Boolean> removeCmdListOption = new HashMap<String, Boolean>(); // if this option (key) is defined in cmdList, merge this string value into the // option's value in pass 2. the value is a string format template into which the // cmdList option value is injected Map<String, String> mergeCmdListOption = new HashMap<String, String>(); // merges that are performed in pass 2 are logged in this map; the key (string) is // the option name and the value is a boolean indicating whether the option was merged // or not. the map is populated after pass 1 with the options from mergeCmdListOption // and all values initialised to false. if an option was merged, it is not appended // to cmdList Map<String, Boolean> mergedCmdListOption = new HashMap<String, Boolean>(); // pass 1: process expertOptions for (int i = 0; i < expertOptions.length; ++i) { if (expertOptions[i].equals("-noass")) { // remove -ass from cmdList in pass 2. // -ass won't have been added in this method (getSpecificCodecOptions // has been called multiple times above to check for -noass and -nomux) // but it may have been added via the renderer or global MEncoder options. // XXX: there are currently 10 other -ass options (-ass-color, -ass-border-color &c.). // technically, they should all be removed... removeCmdListOption.put("-ass", false); // false: option does not have a corresponding value // remove -noass from expertOptions in pass 3 expertOptions[i] = REMOVE_OPTION; } else if (expertOptions[i].equals("-nomux")) { expertOptions[i] = REMOVE_OPTION; } else if (expertOptions[i].equals("-mt")) { // not an MEncoder option so remove it from exportOptions. // multi-threaded MEncoder is used by default, so this is obsolete (TODO: Remove it from the description) expertOptions[i] = REMOVE_OPTION; } else if (expertOptions[i].equals("-ofps")) { // replace the cmdList version with the expertOptions version i.e. remove the former removeCmdListOption.put("-ofps", true); // skip (i.e. leave unchanged) the exportOptions value ++i; } else if (expertOptions[i].equals("-fps")) { removeCmdListOption.put("-fps", true); ++i; } else if (expertOptions[i].equals("-ovc")) { removeCmdListOption.put("-ovc", true); ++i; } else if (expertOptions[i].equals("-channels")) { removeCmdListOption.put("-channels", true); ++i; } else if (expertOptions[i].equals("-oac")) { removeCmdListOption.put("-oac", true); ++i; } else if (expertOptions[i].equals("-quality")) { // XXX like the old (cmdArray) code, this clobbers the old -lavcopts value String lavcopts = String.format( "autoaspect=1:vcodec=%s:acodec=%s:abitrate=%s:threads=%d:%s", vcodec, (configuration.isMencoderAc3Fixed() ? "ac3_fixed" : "ac3"), CodecUtil.getAC3Bitrate(configuration, params.aid), configuration.getMencoderMaxThreads(), expertOptions[i + 1]); // append bitrate-limiting options if configured lavcopts = addMaximumBitrateConstraints(lavcopts, media, lavcopts, params.mediaRenderer, ""); // a string format with no placeholders, so the cmdList option value is ignored. // note: we protect "%" from being interpreted as a format by converting it to "%%", // which is then turned back into "%" when the format is processed mergeCmdListOption.put("-lavcopts", lavcopts.replace("%", "%%")); // remove -quality <value> expertOptions[i] = expertOptions[i + 1] = REMOVE_OPTION; ++i; } else if (expertOptions[i].equals("-mpegopts")) { mergeCmdListOption.put("-mpegopts", "%s:" + expertOptions[i + 1].replace("%", "%%")); // merge if cmdList already contains -mpegopts, but don't append if it doesn't (parity with the old (cmdArray) version) expertOptions[i] = expertOptions[i + 1] = REMOVE_OPTION; ++i; } else if (expertOptions[i].equals("-vf")) { mergeCmdListOption.put("-vf", "%s," + expertOptions[i + 1].replace("%", "%%")); ++i; } else if (expertOptions[i].equals("-af")) { mergeCmdListOption.put("-af", "%s," + expertOptions[i + 1].replace("%", "%%")); ++i; } else if (expertOptions[i].equals("-nosync")) { disableMc0AndNoskip = true; expertOptions[i] = REMOVE_OPTION; } else if (expertOptions[i].equals("-mc")) { disableMc0AndNoskip = true; } } for (String key : mergeCmdListOption.keySet()) { mergedCmdListOption.put(key, false); } // pass 2: process cmdList List<String> transformedCmdList = new ArrayList<String>(); for (int i = 0; i < cmdList.size(); ++i) { String option = cmdList.get(i); // we remove an option by *not* adding it to transformedCmdList if (removeCmdListOption.containsKey(option)) { if (isTrue(removeCmdListOption.get(option))) { // true: remove (i.e. don't add) the corresponding value ++i; } } else { transformedCmdList.add(option); if (mergeCmdListOption.containsKey(option)) { String format = mergeCmdListOption.get(option); String value = String.format(format, cmdList.get(i + 1)); // record the fact that an expertOption value has been merged into this cmdList value mergedCmdListOption.put(option, true); transformedCmdList.add(value); ++i; } } } cmdList = transformedCmdList; // pass 3: append expertOptions to cmdList for (int i = 0; i < expertOptions.length; ++i) { String option = expertOptions[i]; if (option != REMOVE_OPTION) { if (isTrue(mergedCmdListOption.get(option))) { // true: this option and its value have already been merged into existing cmdList options ++i; // skip the value } else { cmdList.add(option); } } } } } if ((pcm || dtsRemux || ac3Remux) || (configuration.isMencoderNoOutOfSync() && !disableMc0AndNoskip)) { if (configuration.isFix25FPSAvMismatch()) { cmdList.add("-mc"); cmdList.add("0.005"); } else { cmdList.add("-mc"); cmdList.add("0"); cmdList.add("-noskip"); } } if (params.timeend > 0) { cmdList.add("-endpos"); cmdList.add("" + params.timeend); } String rate = "48000"; if (params.mediaRenderer.isXBOX()) { rate = "44100"; } // force srate -> cause ac3's mencoder doesn't like anything other than 48khz if (media != null && !pcm && !dtsRemux && !ac3Remux) { cmdList.add("-af"); cmdList.add("lavcresample=" + rate); cmdList.add("-srate"); cmdList.add(rate); } // add a -cache option for piped media (e.g. rar/zip file entries): // https://code.google.com/p/ps3mediaserver/issues/detail?id=911 if (params.stdin != null) { cmdList.add("-cache"); cmdList.add("8192"); } PipeProcess pipe = null; ProcessWrapperImpl pw = null; if (pcm || dtsRemux) { // transcode video, demux audio, remux with tsmuxer boolean channels_filter_present = false; for (String s : cmdList) { if (isNotBlank(s) && s.startsWith("channels")) { channels_filter_present = true; break; } } if (params.avidemux) { pipe = new PipeProcess("mencoder" + System.currentTimeMillis(), (pcm || dtsRemux || ac3Remux) ? null : params); params.input_pipes[0] = pipe; cmdList.add("-o"); cmdList.add(pipe.getInputPipe()); if (pcm && !channels_filter_present && params.aid != null) { String mixer = getLPCMChannelMappingForMencoder(params.aid); if (isNotBlank(mixer)) { cmdList.add("-af"); cmdList.add(mixer); } } String[] cmdArray = new String[cmdList.size()]; cmdList.toArray(cmdArray); pw = new ProcessWrapperImpl(cmdArray, params); PipeProcess videoPipe = new PipeProcess("videoPipe" + System.currentTimeMillis(), "out", "reconnect"); PipeProcess audioPipe = new PipeProcess("audioPipe" + System.currentTimeMillis(), "out", "reconnect"); ProcessWrapper videoPipeProcess = videoPipe.getPipeProcess(); ProcessWrapper audioPipeProcess = audioPipe.getPipeProcess(); params.output_pipes[0] = videoPipe; params.output_pipes[1] = audioPipe; pw.attachProcess(videoPipeProcess); pw.attachProcess(audioPipeProcess); videoPipeProcess.runInNewThread(); audioPipeProcess.runInNewThread(); try { Thread.sleep(50); } catch (InterruptedException e) { } videoPipe.deleteLater(); audioPipe.deleteLater(); } else { // remove the -oac switch, otherwise the "too many video packets" errors appear again for (ListIterator<String> it = cmdList.listIterator(); it.hasNext();) { String option = it.next(); if (option.equals("-oac")) { it.set("-nosound"); if (it.hasNext()) { it.next(); it.remove(); } break; } } pipe = new PipeProcess(System.currentTimeMillis() + "tsmuxerout.ts"); TsMuxeRVideo ts = new TsMuxeRVideo(configuration); File f = new File(configuration.getTempFolder(), "pms-tsmuxer.meta"); String cmd[] = new String[] { ts.executable(), f.getAbsolutePath(), pipe.getInputPipe() }; pw = new ProcessWrapperImpl(cmd, params); PipeIPCProcess ffVideoPipe = new PipeIPCProcess(System.currentTimeMillis() + "ffmpegvideo", System.currentTimeMillis() + "videoout", false, true); cmdList.add("-o"); cmdList.add(ffVideoPipe.getInputPipe()); OutputParams ffparams = new OutputParams(configuration); ffparams.maxBufferSize = 1; ffparams.stdin = params.stdin; String[] cmdArray = new String[cmdList.size()]; cmdList.toArray(cmdArray); ProcessWrapperImpl ffVideo = new ProcessWrapperImpl(cmdArray, ffparams); ProcessWrapper ff_video_pipe_process = ffVideoPipe.getPipeProcess(); pw.attachProcess(ff_video_pipe_process); ff_video_pipe_process.runInNewThread(); ffVideoPipe.deleteLater(); pw.attachProcess(ffVideo); ffVideo.runInNewThread(); String aid = null; if (media != null && media.getAudioTracksList().size() > 1 && params.aid != null) { if (media.getContainer() != null && (media.getContainer().equals(FormatConfiguration.AVI) || media.getContainer().equals(FormatConfiguration.FLV))) { // TODO confirm (MP4s, OGMs and MOVs already tested: first aid is 0; AVIs: first aid is 1) // for AVIs, FLVs and MOVs mencoder starts audio tracks numbering from 1 aid = "" + (params.aid.getId() + 1); } else { // everything else from 0 aid = "" + params.aid.getId(); } } PipeIPCProcess ffAudioPipe = new PipeIPCProcess(System.currentTimeMillis() + "ffmpegaudio01", System.currentTimeMillis() + "audioout", false, true); StreamModifier sm = new StreamModifier(); sm.setPcm(pcm); sm.setDtsEmbed(dtsRemux); sm.setSampleFrequency(48000); sm.setBitsPerSample(16); String mixer = null; if (pcm && !dtsRemux) { mixer = getLPCMChannelMappingForMencoder(params.aid); // LPCM always outputs 5.1/7.1 for multichannel tracks. Downmix with player if needed! } sm.setNbChannels(channels); // it seems the -really-quiet prevents mencoder to stop the pipe output after some time... // -mc 0.1 make the DTS-HD extraction works better with latest mencoder builds, and makes no impact on the regular DTS one String ffmpegLPCMextract[] = new String[] { executable(), "-ss", "0", filename, "-really-quiet", "-msglevel", "statusline=2", "-channels", "" + channels, "-ovc", "copy", "-of", "rawaudio", "-mc", dtsRemux ? "0.1" : "0", "-noskip", (aid == null) ? "-quiet" : "-aid", (aid == null) ? "-quiet" : aid, "-oac", (ac3Remux || dtsRemux) ? "copy" : "pcm", (isNotBlank(mixer) && !channels_filter_present) ? "-af" : "-quiet", (isNotBlank(mixer) && !channels_filter_present) ? mixer : "-quiet", "-srate", "48000", "-o", ffAudioPipe.getInputPipe() }; if (!params.mediaRenderer.isMuxDTSToMpeg()) { // no need to use the PCM trick when media renderer supports DTS ffAudioPipe.setModifier(sm); } if (media != null && media.getDvdtrack() > 0) { ffmpegLPCMextract[3] = "-dvd-device"; ffmpegLPCMextract[4] = filename; ffmpegLPCMextract[5] = "dvd://" + media.getDvdtrack(); } else if (params.stdin != null) { ffmpegLPCMextract[3] = "-"; } if (filename.toLowerCase().endsWith(".evo")) { ffmpegLPCMextract[4] = "-psprobe"; ffmpegLPCMextract[5] = "1000000"; } if (params.timeseek > 0) { ffmpegLPCMextract[2] = "" + params.timeseek; } OutputParams ffaudioparams = new OutputParams(configuration); ffaudioparams.maxBufferSize = 1; ffaudioparams.stdin = params.stdin; ProcessWrapperImpl ffAudio = new ProcessWrapperImpl(ffmpegLPCMextract, ffaudioparams); params.stdin = null; PrintWriter pwMux = new PrintWriter(f); pwMux.println("MUXOPT --no-pcr-on-video-pid --no-asyncio --new-audio-pes --vbr --vbv-len=500"); String videoType = "V_MPEG-2"; if (params.no_videoencode && params.forceType != null) { videoType = params.forceType; } String fps = ""; if (params.forceFps != null) { fps = "fps=" + params.forceFps + ", "; } String audioType; if (ac3Remux) { audioType = "A_AC3"; } else if (dtsRemux) { if (params.mediaRenderer.isMuxDTSToMpeg()) { //renderer can play proper DTS track audioType = "A_DTS"; } else { // DTS padded in LPCM trick audioType = "A_LPCM"; } } else { // PCM audioType = "A_LPCM"; } // mencoder bug (confirmed with mencoder r35003 + ffmpeg 0.11.1): // audio delay is ignored when playing from file start (-ss 0) // override with tsmuxer.meta setting String timeshift = ""; if (mencoderAC3RemuxAudioDelayBug) { timeshift = "timeshift=" + params.aid.getAudioProperties().getAudioDelay() + "ms, "; } pwMux.println(videoType + ", \"" + ffVideoPipe.getOutputPipe() + "\", " + fps + "level=4.1, insertSEI, contSPS, track=1"); pwMux.println(audioType + ", \"" + ffAudioPipe.getOutputPipe() + "\", " + timeshift + "track=2"); pwMux.close(); ProcessWrapper pipe_process = pipe.getPipeProcess(); pw.attachProcess(pipe_process); pipe_process.runInNewThread(); try { Thread.sleep(50); } catch (InterruptedException e) { } pipe.deleteLater(); params.input_pipes[0] = pipe; ProcessWrapper ff_pipe_process = ffAudioPipe.getPipeProcess(); pw.attachProcess(ff_pipe_process); ff_pipe_process.runInNewThread(); try { Thread.sleep(50); } catch (InterruptedException e) { } ffAudioPipe.deleteLater(); pw.attachProcess(ffAudio); ffAudio.runInNewThread(); } } else { boolean directpipe = Platform.isMac() || Platform.isFreeBSD(); if (directpipe) { cmdList.add("-o"); cmdList.add("-"); cmdList.add("-really-quiet"); cmdList.add("-msglevel"); cmdList.add("statusline=2"); params.input_pipes = new PipeProcess[2]; } else { pipe = new PipeProcess("mencoder" + System.currentTimeMillis(), (pcm || dtsRemux) ? null : params); params.input_pipes[0] = pipe; cmdList.add("-o"); cmdList.add(pipe.getInputPipe()); } String[] cmdArray = new String[cmdList.size()]; cmdList.toArray(cmdArray); cmdArray = finalizeTranscoderArgs(filename, dlna, media, params, cmdArray); pw = new ProcessWrapperImpl(cmdArray, params); if (!directpipe) { ProcessWrapper mkfifo_process = pipe.getPipeProcess(); pw.attachProcess(mkfifo_process); mkfifo_process.runInNewThread(); try { Thread.sleep(50); } catch (InterruptedException e) { } pipe.deleteLater(); } } pw.runInNewThread(); try { Thread.sleep(100); } catch (InterruptedException e) { } return pw; }