import static org.jcodec.common.Tuple._2; import static org.jcodec.common.Tuple._3; import org.jcodec.common.Codec; import org.jcodec.common.Demuxer; import org.jcodec.common.DemuxerTrack; import org.jcodec.common.DemuxerTrackMeta; import org.jcodec.common.Format; import org.jcodec.common.JCodecUtil; import org.jcodec.common.TrackType; import org.jcodec.common.logging.Logger; import org.jcodec.common.model.Packet; import org.jcodec.common.tools.MainUtils; import org.jcodec.common.tools.MainUtils.Cmd; import org.jcodec.common.tools.MathUtil; import org.jcodec.samples.transcode.Transcoder.Filter; import org.jcodec.samples.transcode.filters.DumpMvFilter; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Transcoder command line. * * @author The JCodec project * */ sclass TranscodeMain { private static final String FLAG_SEEK_FRAMES = "seek-frames"; private static final String FLAG_MAX_FRAMES = "max-frames"; private static final String FLAG_OUTPUT_AUDIO_CODEC = "o:ac"; private static final String FLAG_INPUT_AUDIO_CODEC = "i:ac"; private static final String FLAG_OUTPUT_VIDEO_CODEC = "o:vc"; private static final String FLAG_INPUT_VIDEO_CODEC = "i:vc"; private static final String FLAG_OUTPUT_FORMAT = "o:f"; private static final String FLAG_INPUT_FORMAT = "i:f"; private static final String FLAG_PROFILE = "profile"; private static final String FLAG_INTERLACED = "interlaced"; private static final String FLAG_DUMPMV = "dumpMv"; private static final String FLAG_DUMPMVJS = "dumpMvJs"; private static final String FLAG_DOWNSCALE = "downscale"; private static Map extensionToF = new HashMap(); private static Map extensionToC = new HashMap(); private static Map videoCodecsForF = new HashMap(); private static Map audioCodecsForF = new HashMap(); private static Set supportedDecoders = new HashSet(); static { extensionToF.put("mpg", Format.MPEG_PS); extensionToF.put("mpeg", Format.MPEG_PS); extensionToF.put("m2p", Format.MPEG_PS); extensionToF.put("ps", Format.MPEG_PS); extensionToF.put("vob", Format.MPEG_PS); extensionToF.put("evo", Format.MPEG_PS); extensionToF.put("mod", Format.MPEG_PS); extensionToF.put("tod", Format.MPEG_PS); extensionToF.put("ts", Format.MPEG_TS); extensionToF.put("m2t", Format.MPEG_TS); extensionToF.put("mp4", Format.MOV); extensionToF.put("m4a", Format.MOV); extensionToF.put("m4v", Format.MOV); extensionToF.put("mov", Format.MOV); extensionToF.put("3gp", Format.MOV); extensionToF.put("mkv", Format.MKV); extensionToF.put("webm", Format.MKV); extensionToF.put("264", Format.H264); extensionToF.put("raw", Format.RAW); extensionToF.put("", Format.RAW); extensionToF.put("flv", Format.FLV); extensionToF.put("avi", Format.AVI); extensionToF.put("jpg", Format.IMG); extensionToF.put("jpeg", Format.IMG); extensionToF.put("png", Format.IMG); extensionToF.put("mjp", Format.MJPEG); extensionToF.put("ivf", Format.IVF); extensionToF.put("y4m", Format.Y4M); extensionToF.put("wav", Format.WAV); extensionToC.put("mpg", Codec.MPEG2); extensionToC.put("mpeg", Codec.MPEG2); extensionToC.put("m2p", Codec.MPEG2); extensionToC.put("ps", Codec.MPEG2); extensionToC.put("vob", Codec.MPEG2); extensionToC.put("evo", Codec.MPEG2); extensionToC.put("mod", Codec.MPEG2); extensionToC.put("tod", Codec.MPEG2); extensionToC.put("ts", Codec.MPEG2); extensionToC.put("m2t", Codec.MPEG2); extensionToC.put("m4a", Codec.AAC); extensionToC.put("mkv", Codec.H264); extensionToC.put("webm", Codec.VP8); extensionToC.put("264", Codec.H264); extensionToC.put("raw", Codec.RAW); extensionToC.put("jpg", Codec.JPEG); extensionToC.put("jpeg", Codec.JPEG); extensionToC.put("png", Codec.PNG); extensionToC.put("mjp", Codec.JPEG); extensionToC.put("y4m", Codec.RAW); videoCodecsForF.put(Format.MPEG_PS, Codec.MPEG2); audioCodecsForF.put(Format.MPEG_PS, Codec.MP2); videoCodecsForF.put(Format.MOV, Codec.H264); audioCodecsForF.put(Format.MOV, Codec.AAC); videoCodecsForF.put(Format.MKV, Codec.VP8); audioCodecsForF.put(Format.MKV, Codec.VORBIS); audioCodecsForF.put(Format.WAV, Codec.PCM); videoCodecsForF.put(Format.H264, Codec.H264); videoCodecsForF.put(Format.RAW, Codec.RAW); videoCodecsForF.put(Format.FLV, Codec.H264); videoCodecsForF.put(Format.AVI, Codec.MPEG4); videoCodecsForF.put(Format.IMG, Codec.PNG); videoCodecsForF.put(Format.MJPEG, Codec.JPEG); videoCodecsForF.put(Format.IVF, Codec.VP8); videoCodecsForF.put(Format.Y4M, Codec.RAW); supportedDecoders.add(Codec.AAC); supportedDecoders.add(Codec.H264); supportedDecoders.add(Codec.JPEG); supportedDecoders.add(Codec.MPEG2); supportedDecoders.add(Codec.PCM); supportedDecoders.add(Codec.PNG); supportedDecoders.add(Codec.PRORES); supportedDecoders.add(Codec.RAW); supportedDecoders.add(Codec.VP8); } public static void main(String[] args) throws Exception { Cmd cmd = MainUtils.parseArguments(args); if (args.length < 2) { MainUtils.printHelpVarArgs(new HashMap() { { put(FLAG_INPUT_FORMAT, "Input format [default=auto]."); put(FLAG_OUTPUT_FORMAT, "Output format [default=auto]."); put(FLAG_INPUT_VIDEO_CODEC, "Input video codec [default=auto]."); put(FLAG_OUTPUT_VIDEO_CODEC, "Output video codec [default=auto]."); put(FLAG_INPUT_AUDIO_CODEC, "Input audio codec [default=auto]."); put(FLAG_OUTPUT_AUDIO_CODEC, "Output audio codec [default=auto]."); put(FLAG_SEEK_FRAMES, "Seek frames"); put(FLAG_MAX_FRAMES, "Max frames"); put(FLAG_PROFILE, "Profile to use (supported by some encoders)."); put(FLAG_INTERLACED, "Encode output as interlaced (supported by Prores encoder)."); put(FLAG_DUMPMV, "Dump motion vectors (supported by h.264 decoder)."); put(FLAG_DUMPMVJS, "Dump motion vectors in form of JASON file (supported by h.264 decoder)."); put(FLAG_DOWNSCALE, "Decode frames in downscale (supported by MPEG, Prores and Jpeg decoders)."); } }, "input", "output"); return; } String input = cmd.getArg(0); String output = cmd.getArg(1); String inputFormatRaw = cmd.getStringFlag(FLAG_INPUT_FORMAT); Format inputFormat; if (inputFormatRaw == null) { inputFormat = getFormatFromExtension(input); if (inputFormat != Format.IMG) { Format detectFormat = JCodecUtil.detectFormat(new File(input)); if (detectFormat != null) inputFormat = detectFormat; } } else { inputFormat = Format.valueOf(inputFormatRaw.toUpperCase()); } if (inputFormat == null) { Logger.error("Input format could not be detected"); return; } String outputFormatRaw = cmd.getStringFlag(FLAG_OUTPUT_FORMAT); Format outputFormat; if (outputFormatRaw == null) { outputFormat = getFormatFromExtension(output); } else { outputFormat = Format.valueOf(outputFormatRaw.toUpperCase()); } int videoTrackNo = -1; String inputCodecVideoRaw = cmd.getStringFlag(FLAG_INPUT_VIDEO_CODEC); _3 inputCodecVideo = null; if (inputCodecVideoRaw == null) { if (inputFormat == Format.IMG) { inputCodecVideo = _3(0, 0, getCodecFromExtension(input)); } else if (inputFormat.isVideo()) { inputCodecVideo = selectSuitableTrack(input, inputFormat, TrackType.VIDEO); } } else { inputCodecVideo = _3(0, 0, Codec.valueOf(inputCodecVideoRaw.toUpperCase())); } String outputCodecVideoRaw = cmd.getStringFlag(FLAG_OUTPUT_VIDEO_CODEC); Codec outputCodecVideo = null; boolean videoCopy = false; if (outputCodecVideoRaw == null) { outputCodecVideo = getCodecFromExtension(output); if (outputCodecVideo == null) outputCodecVideo = getFirstVideoCodecForFormat(outputFormat); } else { if ("copy".equalsIgnoreCase(outputCodecVideoRaw)) { videoCopy = true; outputCodecVideo = inputCodecVideo.v2; } else if("none".equalsIgnoreCase(outputCodecVideoRaw)) { outputCodecVideo = null; inputCodecVideo = null; } else { outputCodecVideo = Codec.valueOf(outputCodecVideoRaw.toUpperCase()); } } String inputCodecAudioRaw = cmd.getStringFlag(FLAG_INPUT_AUDIO_CODEC); _3 inputCodecAudio = null; if (inputCodecAudioRaw == null) { if (inputFormat.isAudio()) { inputCodecAudio = selectSuitableTrack(input, inputFormat, TrackType.AUDIO); } } else { inputCodecAudio = _3(0, 0, Codec.valueOf(inputCodecAudioRaw.toUpperCase())); } String outputCodecAudioRaw = cmd.getStringFlag(FLAG_OUTPUT_AUDIO_CODEC); Codec outputCodecAudio = null; boolean audioCopy = false; if (outputCodecAudioRaw == null) { if (outputFormat.isAudio()) outputCodecAudio = getFirstAudioCodecForFormat(outputFormat); } else { if ("copy".equalsIgnoreCase(outputCodecAudioRaw)) { audioCopy = true; outputCodecAudio = inputCodecAudio.v2; } else if("none".equalsIgnoreCase(outputCodecVideoRaw)) { outputCodecAudio = null; inputCodecAudio = null; } else { outputCodecAudio = Codec.valueOf(outputCodecAudioRaw.toUpperCase()); } } if (inputCodecAudio == null) outputCodecAudio = null; List filters = new ArrayList(); if (cmd.getBooleanFlag(FLAG_DUMPMV)) filters.add(new DumpMvFilter(false)); else if (cmd.getBooleanFlag(FLAG_DUMPMVJS)) filters.add(new DumpMvFilter(true)); Transcoder transcoder = new Transcoder(cmd.getArg(0), cmd.getArg(1), inputFormat, outputFormat, inputCodecVideo, outputCodecVideo, inputCodecAudio, outputCodecAudio, videoCopy, audioCopy, filters); transcoder.setSeekFrames(cmd.getIntegerFlagD(FLAG_SEEK_FRAMES, 0)); transcoder.setMaxFrames(cmd.getIntegerFlagD(FLAG_MAX_FRAMES, Integer.MAX_VALUE)); transcoder.setProfile(cmd.getStringFlag(FLAG_PROFILE)); transcoder.setInterlaced(cmd.getBooleanFlagD(FLAG_INTERLACED, false)); Integer downscale = cmd.getIntegerFlagD(FLAG_DOWNSCALE, 1); if (downscale != null && (1 << MathUtil.log2(downscale)) != downscale) { Logger.error( "Only values [2, 4, 8] are supported for " + FLAG_DOWNSCALE + ", the option will have no effect."); } else { transcoder.setDownscale(downscale); } transcoder.transcode(); } private static Codec getFirstAudioCodecForFormat(Format inputFormat) { return audioCodecsForF.get(inputFormat); } private static Codec getFirstVideoCodecForFormat(Format inputFormat) { return videoCodecsForF.get(inputFormat); } private static Codec detectVideoDecoder(DemuxerTrack track) throws IOException { DemuxerTrackMeta meta = track.getMeta(); if (meta != null) { Codec codec = meta.getCodec(); if (codec != null) return codec; } Packet packet = track.nextFrame(); if (packet == null) return null; return JCodecUtil.detectDecoder(packet.getData()); } private static _3 selectSuitableTrack(String input, Format format, TrackType targetType) throws IOException { _2 demuxerPid; if (format == Format.MPEG_TS) { demuxerPid = JCodecUtil.createM2TSDemuxer(new File(input), targetType); } else { demuxerPid = _2(0, JCodecUtil.createDemuxer(format, new File(input))); } if(demuxerPid == null || demuxerPid.v1 == null) return null; int trackNo = 0; List tracks = targetType == TrackType.VIDEO ? demuxerPid.v1.getVideoTracks() : demuxerPid.v1.getAudioTracks(); for (DemuxerTrack demuxerTrack : tracks) { Codec codec = detectVideoDecoder(demuxerTrack); if (supportedDecoders.contains(codec)) { return _3(demuxerPid.v0, trackNo, codec); } trackNo++; } return null; } private static Format getFormatFromExtension(String output) { String extension = output.replaceFirst(".*\\.([^\\.]+$)", "$1"); return extensionToF.get(extension); } private static Codec getCodecFromExtension(String output) { String extension = output.replaceFirst(".*\\.([^\\.]+$)", "$1"); return extensionToC.get(extension); } public static Set formats(Format... formats) { return new HashSet(Arrays.asList(formats)); } public static Set codecs(Codec... codecs) { return new HashSet(Arrays.asList(codecs)); } }