com.liferay.portlet.documentlibrary.util.LiferayVideoConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.portlet.documentlibrary.util.LiferayVideoConverter.java

Source

/**
 * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 *
 * This library 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 Lesser General Public License for more
 * details.
 */

package com.liferay.portlet.documentlibrary.util;

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;

import com.xuggle.xuggler.Configuration;
import com.xuggle.xuggler.IAudioResampler;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IContainerFormat;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IPixelFormat.Type;
import com.xuggle.xuggler.IRational;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
import com.xuggle.xuggler.IVideoPicture;
import com.xuggle.xuggler.IVideoResampler;

import java.io.File;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * @author Juan Gonzlez
 * @author Sergio Gonzlez
 * @author Brian Wing Shun Chan
 * @author Alexander Chow
 */
public class LiferayVideoConverter extends LiferayConverter {

    public LiferayVideoConverter(String inputURL, String outputURL, String tempFileName, Properties videoProperties,
            Properties ffpresetProperties) {

        _inputURL = inputURL;
        _outputURL = outputURL;
        _tempFileName = tempFileName;
        _ffpresetProperties = ffpresetProperties;

        _height = GetterUtil.getInteger(videoProperties.getProperty(PropsKeys.DL_FILE_ENTRY_PREVIEW_VIDEO_HEIGHT),
                _height);
        _width = GetterUtil.getInteger(videoProperties.getProperty(PropsKeys.DL_FILE_ENTRY_PREVIEW_VIDEO_WIDTH),
                _width);
        _previewVideoContainers = StringUtil
                .split(videoProperties.getProperty(PropsKeys.DL_FILE_ENTRY_PREVIEW_VIDEO_CONTAINERS));

        initVideoBitRateMap(videoProperties);
        initVideoFrameRateMap(videoProperties);
    }

    @Override
    public void convert() throws Exception {
        try {
            doConvert();
        } finally {
            if (_inputIContainer.isOpened()) {
                _inputIContainer.close();
            }

            if (_outputIContainer.isOpened()) {
                _outputIContainer.close();
            }
        }

        createMP4FastStart();
    }

    protected void createMP4FastStart() {
        File videoFile = new File(_outputURL);

        if (_outputVideoFormat.equals("mp4") && videoFile.exists()) {
            File tempFile = new File(_tempFileName);

            try {
                JQTFastStart.convert(videoFile, tempFile);

                if (tempFile.exists() && tempFile.length() > 0) {
                    videoFile.delete();

                    tempFile.renameTo(videoFile);
                }
            } catch (Exception e) {
                if (_log.isWarnEnabled()) {
                    _log.warn("Unable to move MOOV atom to front of MP4 file");
                }
            } finally {
                tempFile.delete();
            }
        }
    }

    protected void doConvert() throws Exception {
        _inputIContainer = IContainer.make();
        _outputIContainer = IContainer.make();

        openContainer(_inputIContainer, _inputURL, false);
        openContainer(_outputIContainer, _outputURL, true);

        int inputStreamsCount = _inputIContainer.getNumStreams();

        if (inputStreamsCount < 0) {
            throw new RuntimeException("Input URL does not have any streams");
        }

        IContainerFormat iContainerFormat = _outputIContainer.getContainerFormat();

        _outputVideoFormat = iContainerFormat.getOutputFormatShortName();

        IAudioResampler[] iAudioResamplers = new IAudioResampler[inputStreamsCount];
        IVideoResampler[] iVideoResamplers = new IVideoResampler[inputStreamsCount];

        IAudioSamples[] inputIAudioSamples = new IAudioSamples[inputStreamsCount];
        IAudioSamples[] outputIAudioSamples = new IAudioSamples[inputStreamsCount];

        IVideoPicture[] inputIVideoPictures = new IVideoPicture[inputStreamsCount];
        IVideoPicture[] outputIVideoPictures = new IVideoPicture[inputStreamsCount];

        IStream[] outputIStreams = new IStream[inputStreamsCount];

        IStreamCoder[] inputIStreamCoders = new IStreamCoder[inputStreamsCount];
        IStreamCoder[] outputIStreamCoders = new IStreamCoder[inputStreamsCount];

        for (int i = 0; i < inputStreamsCount; i++) {
            IStream inputIStream = _inputIContainer.getStream(i);

            IStreamCoder inputIStreamCoder = inputIStream.getStreamCoder();

            inputIStreamCoders[i] = inputIStreamCoder;

            ICodec.Type inputICodecType = inputIStreamCoder.getCodecType();

            if (inputICodecType == ICodec.Type.CODEC_TYPE_AUDIO) {
                prepareAudio(iAudioResamplers, inputIAudioSamples, outputIAudioSamples, inputIStreamCoder,
                        outputIStreamCoders, _outputIContainer, outputIStreams, inputICodecType, _outputURL, i);
            } else if (inputICodecType == ICodec.Type.CODEC_TYPE_VIDEO) {
                prepareVideo(iVideoResamplers, inputIVideoPictures, outputIVideoPictures, inputIStreamCoder,
                        outputIStreamCoders, _outputIContainer, outputIStreams, inputICodecType, _outputURL, i);
            }

            openStreamCoder(inputIStreamCoders[i]);
            openStreamCoder(outputIStreamCoders[i]);
        }

        if (_outputIContainer.writeHeader() < 0) {
            throw new RuntimeException("Unable to write container header");
        }

        boolean keyPacketFound = false;
        int nonKeyAfterKeyCount = 0;
        boolean onlyDecodeKeyPackets = false;
        int previousPacketSize = -1;

        IPacket inputIPacket = IPacket.make();
        IPacket outputIPacket = IPacket.make();

        while (_inputIContainer.readNextPacket(inputIPacket) == 0) {
            if (_log.isDebugEnabled()) {
                _log.debug("Current packet size " + inputIPacket.getSize());
            }

            int streamIndex = inputIPacket.getStreamIndex();

            IStreamCoder inputIStreamCoder = inputIStreamCoders[streamIndex];
            IStreamCoder outputIStreamCoder = outputIStreamCoders[streamIndex];

            if (outputIStreamCoder == null) {
                continue;
            }

            IStream iStream = _inputIContainer.getStream(streamIndex);

            long timeStampOffset = getStreamTimeStampOffset(iStream);

            if (inputIStreamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {

                decodeAudio(iAudioResamplers[streamIndex], inputIAudioSamples[streamIndex],
                        outputIAudioSamples[streamIndex], inputIPacket, outputIPacket, inputIStreamCoder,
                        outputIStreamCoder, _outputIContainer, inputIPacket.getSize(), previousPacketSize,
                        streamIndex, timeStampOffset);
            } else if (inputIStreamCoder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {

                keyPacketFound = isKeyPacketFound(inputIPacket, keyPacketFound);

                nonKeyAfterKeyCount = countNonKeyAfterKey(inputIPacket, keyPacketFound, nonKeyAfterKeyCount);

                if (isStartDecoding(inputIPacket, inputIStreamCoder, keyPacketFound, nonKeyAfterKeyCount,
                        onlyDecodeKeyPackets)) {

                    int value = decodeVideo(iVideoResamplers[streamIndex], inputIVideoPictures[streamIndex],
                            outputIVideoPictures[streamIndex], inputIPacket, outputIPacket, inputIStreamCoder,
                            outputIStreamCoder, _outputIContainer, null, null, 0, 0, timeStampOffset);

                    if (value <= 0) {
                        if (inputIPacket.isKey()) {
                            throw new RuntimeException("Unable to decode video stream " + streamIndex);
                        }

                        onlyDecodeKeyPackets = true;

                        continue;
                    }
                } else {
                    if (_log.isDebugEnabled()) {
                        _log.debug("Do not decode video stream " + streamIndex);
                    }
                }
            }

            previousPacketSize = inputIPacket.getSize();
        }

        flush(outputIStreamCoders, _outputIContainer);

        if (_outputIContainer.writeTrailer() < 0) {
            throw new RuntimeException("Unable to write trailer to output file");
        }

        cleanUp(iAudioResamplers, iVideoResamplers);
        cleanUp(inputIAudioSamples, outputIAudioSamples);
        cleanUp(inputIVideoPictures, outputIVideoPictures);
        cleanUp(inputIStreamCoders, outputIStreamCoders);
        cleanUp(inputIPacket, outputIPacket);
    }

    @Override
    protected int getAudioEncodingChannels(IContainer outputIContainer, int channels) {

        IContainerFormat iContainerFormat = outputIContainer.getContainerFormat();

        String outputFormat = iContainerFormat.getOutputFormatShortName();

        if (outputFormat.equals("ogg")) {
            return 2;
        }

        return super.getAudioEncodingChannels(outputIContainer, channels);
    }

    @Override
    protected ICodec getAudioEncodingICodec(IContainer outputIContainer) {
        IContainerFormat iContainerFormat = outputIContainer.getContainerFormat();

        String outputFormat = iContainerFormat.getOutputFormatShortName();

        if (outputFormat.equals("ogg")) {
            return ICodec.findEncodingCodec(ICodec.ID.CODEC_ID_VORBIS);
        }

        return super.getAudioEncodingICodec(outputIContainer);
    }

    @Override
    protected IContainer getInputIContainer() {
        return _inputIContainer;
    }

    protected void initVideoBitRateMap(Properties videoProperties) {
        _videoBitRateMap = new HashMap<String, Integer>();

        for (String previewVideoContainer : _previewVideoContainers) {
            int videoBitRate = GetterUtil.getInteger(videoProperties.getProperty(
                    PropsKeys.DL_FILE_ENTRY_PREVIEW_VIDEO_BIT_RATE + "[" + previewVideoContainer + "]"));

            if (videoBitRate > _VIDEO_BIT_RATE_MAX) {
                videoBitRate = _VIDEO_BIT_RATE_MAX;
            }

            if (videoBitRate > 0) {
                _videoBitRateMap.put(previewVideoContainer, videoBitRate);

                if (_log.isInfoEnabled()) {
                    _log.info("Bit rate for " + previewVideoContainer + " set to " + videoBitRate);
                }
            }
        }
    }

    protected void initVideoFrameRateMap(Properties videoProperties) {
        _videoFrameRateMap = new HashMap<String, IRational>();

        for (String previewVideoContainer : _previewVideoContainers) {
            int numerator = GetterUtil.getInteger(
                    videoProperties.getProperty(PropsKeys.DL_FILE_ENTRY_PREVIEW_VIDEO_FRAME_RATE_NUMERATOR + "["
                            + previewVideoContainer + "]"));
            int denominator = GetterUtil.getInteger(
                    videoProperties.getProperty(PropsKeys.DL_FILE_ENTRY_PREVIEW_VIDEO_FRAME_RATE_DENOMINATOR
                            + StringPool.OPEN_BRACKET + previewVideoContainer + StringPool.CLOSE_BRACKET));

            if ((numerator > 0) && (denominator > 0)) {
                IRational iRational = IRational.make(numerator, denominator);

                _videoFrameRateMap.put(previewVideoContainer, iRational);

                if (_log.isInfoEnabled()) {
                    _log.info("Frame rate for " + previewVideoContainer + " set to " + iRational.getNumerator()
                            + "/" + iRational.getDenominator());
                }
            }
        }
    }

    protected void prepareVideo(IVideoResampler[] iVideoResamplers, IVideoPicture[] inputIVideoPictures,
            IVideoPicture[] outputIVideoPictures, IStreamCoder inputIStreamCoder,
            IStreamCoder[] outputIStreamCoders, IContainer outputIContainer, IStream[] outputIStreams,
            ICodec.Type inputICodecType, String outputURL, int index) throws Exception {

        IStream outputIStream = outputIContainer.addNewStream(index);

        outputIStreams[index] = outputIStream;

        IStreamCoder outputIStreamCoder = outputIStream.getStreamCoder();

        outputIStreamCoders[index] = outputIStreamCoder;

        int bitRate = inputIStreamCoder.getBitRate();

        if (_log.isInfoEnabled()) {
            _log.info("Original video bitrate " + bitRate);
        }

        if (bitRate == 0) {
            bitRate = GetterUtil.getInteger(_videoBitRateMap.get(_outputVideoFormat), _VIDEO_BIT_RATE_DEFAULT);
        } else if (bitRate > _VIDEO_BIT_RATE_MAX) {
            bitRate = _VIDEO_BIT_RATE_MAX;
        }

        if (_log.isInfoEnabled()) {
            _log.info("Modified video bitrate " + bitRate);
        }

        outputIStreamCoder.setBitRate(bitRate);

        ICodec iCodec = ICodec.guessEncodingCodec(null, null, outputURL, null, inputICodecType);

        if (_outputVideoFormat.equals("mp4")) {
            iCodec = ICodec.findEncodingCodec(ICodec.ID.CODEC_ID_H264);
        }

        if (iCodec == null) {
            throw new RuntimeException("Unable to determine " + inputICodecType + " encoder for " + outputURL);
        }

        outputIStreamCoder.setCodec(iCodec);

        IRational iRational = inputIStreamCoder.getFrameRate();

        if (_log.isInfoEnabled()) {
            _log.info("Original frame rate " + iRational.getNumerator() + "/" + iRational.getDenominator());
        }

        if (_videoFrameRateMap.containsKey(_outputVideoFormat)) {
            iRational = _videoFrameRateMap.get(_outputVideoFormat);
        }

        if (_log.isInfoEnabled()) {
            _log.info("Modified frame rate " + iRational.getNumerator() + "/" + iRational.getDenominator());
        }

        outputIStreamCoder.setFrameRate(iRational);

        if (inputIStreamCoder.getHeight() <= 0) {
            throw new RuntimeException("Unable to determine height for " + _inputURL);
        }

        outputIStreamCoder.setHeight(_height);

        outputIStreamCoder.setPixelType(Type.YUV420P);
        outputIStreamCoder.setTimeBase(IRational.make(iRational.getDenominator(), iRational.getNumerator()));

        if (inputIStreamCoder.getWidth() <= 0) {
            throw new RuntimeException("Unable to determine width for " + _inputURL);
        }

        outputIStreamCoder.setWidth(_width);

        iVideoResamplers[index] = createIVideoResampler(inputIStreamCoder, outputIStreamCoder, _height, _width);

        inputIVideoPictures[index] = IVideoPicture.make(inputIStreamCoder.getPixelType(),
                inputIStreamCoder.getWidth(), inputIStreamCoder.getHeight());
        outputIVideoPictures[index] = IVideoPicture.make(outputIStreamCoder.getPixelType(),
                outputIStreamCoder.getWidth(), outputIStreamCoder.getHeight());

        ICodec.ID iCodecID = iCodec.getID();

        if (iCodecID.equals(ICodec.ID.CODEC_ID_H264)) {
            Configuration.configure(_ffpresetProperties, outputIStreamCoder);
        }
    }

    private static final int _VIDEO_BIT_RATE_DEFAULT = 250000;

    private static final int _VIDEO_BIT_RATE_MAX = 1200000;

    private static Log _log = LogFactoryUtil.getLog(LiferayVideoConverter.class);

    private Properties _ffpresetProperties;
    private int _height = 240;
    private IContainer _inputIContainer;
    private String _inputURL;
    private IContainer _outputIContainer;
    private String _outputURL;
    private String _outputVideoFormat;
    private String[] _previewVideoContainers;
    private String _tempFileName;
    private Map<String, Integer> _videoBitRateMap;
    private Map<String, IRational> _videoFrameRateMap;
    private int _width = 320;

}