/* * PS3 Media Server, for streaming any medias to your PS3. * Copyright (C) 2008-2012 A.Brochard * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.pms.encoders; import net.pms.configuration.PmsConfiguration; import net.pms.configuration.RendererConfiguration; import net.pms.dlna.DLNAMediaInfo; import net.pms.dlna.DLNAResource; import net.pms.formats.Format; import net.pms.formats.FormatFactory; import net.pms.formats.WEB; import net.pms.io.OutputParams; import net.pms.io.PipeProcess; import net.pms.io.ProcessWrapper; import net.pms.io.ProcessWrapperImpl; import net.pms.util.PlayerUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.swing.*; import static org.apache.commons.lang3.StringUtils.isNotEmpty; public class FFmpegWebVideo extends FFmpegVideo { private static final Logger logger = LoggerFactory.getLogger(FFmpegWebVideo.class); private final PmsConfiguration configuration; private final FFmpegProtocols protocols; // FIXME we have an id() accessor for this; no need for the field to be public @Deprecated public static final String ID = "ffmpegwebvideo"; public FFmpegWebVideo(PmsConfiguration configuration) { super(configuration); this.configuration = configuration; this.protocols = getProtocols(); // fetch/create the FFmpegProtocols singleton } // use FFmpegWebVideo(PmsConfiguration) @Deprecated public FFmpegWebVideo(PmsConfiguration configuration, FFmpegProtocols protocols) { this(configuration); } @Override public JComponent config() { return null; } @Override public String id() { return ID; } @Override public PlayerPurpose getPurpose() { return PlayerPurpose.VIDEO_WEB_STREAM_PLAYER; } @Override public boolean isTimeSeekable() { return false; } @Override public synchronized ProcessWrapper launchTranscode( DLNAResource dlna, DLNAMediaInfo media, OutputParams params ) throws IOException { params.minBufferSize = params.minFileSize; params.secondread_minsize = 100000; params.waitbeforestart = 6000; RendererConfiguration renderer = params.mediaRenderer; String filename = protocols.getFilename(dlna.getSystemName()); FFmpegOptions customOptions = new FFmpegOptions(); // (HTTP) header options if (params.header != null && params.header.length > 0) { String hdr = new String(params.header); customOptions.addAll(parseOptions(hdr)); } // renderer options if (isNotEmpty(renderer.getCustomFFmpegOptions())) { customOptions.addAll(parseOptions(renderer.getCustomFFmpegOptions())); } // basename of the named pipe: // ffmpeg -global-options -input-options -i URL -output-options /path/to/fifoName 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); pipe.deleteLater(); // delete the named pipe later; harmless if it isn't created 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; // Build the command line final List<String> cmdList = new ArrayList<String>(); cmdList.add(executable()); cmdList.addAll(getGlobalOptions(logger)); int nThreads = configuration.getNumberOfCpuCores(); // Decoder threads cmdList.add("-threads"); cmdList.add("" + nThreads); // Add global and input-file custom options, if any if (!customOptions.isEmpty()) { customOptions.transferGlobals(cmdList); customOptions.transferInputFileOptions(cmdList); } cmdList.add("-i"); cmdList.add(filename); cmdList.addAll(getVideoFilterOptions(dlna, media, params)); // Encoder threads cmdList.add("-threads"); cmdList.add("" + nThreads); // Add the output options (-f, -acodec, -vcodec) 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); } // 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); pw.attachProcess(mkfifo_process); // Clean up the mkfifo process when the transcode ends // 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); } return pw; } @Override public String name() { return "FFmpeg Web Video"; } // TODO remove this when it's removed from Player @Deprecated @Override public String[] args() { return null; } @Override public boolean isCompatible(DLNAResource dlna) { if (!PlayerUtil.isWebVideo(dlna)) { return false; } String protocol = dlna.getFormat().getMatchedExtension(); return protocols.isSupportedProtocol(protocol); } }