001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    
020    import com.xuggle.xuggler.IAudioResampler;
021    import com.xuggle.xuggler.IAudioSamples;
022    import com.xuggle.xuggler.ICodec;
023    import com.xuggle.xuggler.IContainer;
024    import com.xuggle.xuggler.IPacket;
025    import com.xuggle.xuggler.IPixelFormat.Type;
026    import com.xuggle.xuggler.IRational;
027    import com.xuggle.xuggler.IStream;
028    import com.xuggle.xuggler.IStreamCoder;
029    import com.xuggle.xuggler.IVideoPicture;
030    import com.xuggle.xuggler.IVideoResampler;
031    
032    /**
033     * @author Juan González
034     * @author Sergio González
035     * @author Brian Wing Shun Chan
036     */
037    public class LiferayVideoConverter extends LiferayConverter {
038    
039            public LiferayVideoConverter(
040                    String inputURL, String outputURL, int height, int width, int rate) {
041    
042                    _inputURL = inputURL;
043                    _outputURL = outputURL;
044                    _height = height;
045                    _width = width;
046                    _rate = rate;
047            }
048    
049            public void convert() throws Exception {
050                    try {
051                            doConvert();
052                    }
053                    finally {
054                            if (_inputIContainer.isOpened()) {
055                                    _inputIContainer.close();
056                            }
057    
058                            if (_outputIContainer.isOpened()) {
059                                    _outputIContainer.close();
060                            }
061                    }
062            }
063    
064            protected void doConvert() throws Exception {
065                    _inputIContainer = IContainer.make();
066                    _outputIContainer = IContainer.make();
067    
068                    openContainer(_inputIContainer, _inputURL, false);
069                    openContainer(_outputIContainer, _outputURL, true);
070    
071                    int inputStreamsCount = _inputIContainer.getNumStreams();
072    
073                    if (inputStreamsCount < 0) {
074                            throw new RuntimeException("Input URL does not have any streams");
075                    }
076    
077                    IAudioResampler[] iAudioResamplers =
078                            new IAudioResampler[inputStreamsCount];
079                    IVideoResampler[] iVideoResamplers =
080                            new IVideoResampler[inputStreamsCount];
081    
082                    IAudioSamples[] inputIAudioSamples =
083                            new IAudioSamples[inputStreamsCount];
084                    IAudioSamples[] outputIAudioSamples =
085                            new IAudioSamples[inputStreamsCount];
086    
087                    IVideoPicture[] inputIVideoPictures =
088                            new IVideoPicture[inputStreamsCount];
089                    IVideoPicture[] outputIVideoPictures =
090                            new IVideoPicture[inputStreamsCount];
091    
092                    IStream[] outputIStreams = new IStream[inputStreamsCount];
093    
094                    IStreamCoder[] inputIStreamCoders = new IStreamCoder[inputStreamsCount];
095                    IStreamCoder[] outputIStreamCoders =
096                            new IStreamCoder[inputStreamsCount];
097    
098                    for (int i = 0; i < inputStreamsCount; i++) {
099                            IStream inputIStream = _inputIContainer.getStream(i);
100    
101                            IStreamCoder inputIStreamCoder = inputIStream.getStreamCoder();
102    
103                            inputIStreamCoders[i] = inputIStreamCoder;
104    
105                            ICodec.Type inputICodecType = inputIStreamCoder.getCodecType();
106    
107                            if (inputICodecType == ICodec.Type.CODEC_TYPE_AUDIO) {
108                                    int channels = inputIStreamCoder.getChannels();
109    
110                                    if (_channels > 0) {
111                                            channels = _channels;
112                                    }
113    
114                                    int rate = inputIStreamCoder.getSampleRate();
115    
116                                    if (_rate > 0) {
117                                            rate = _rate;
118                                    }
119    
120                                    prepareAudio(
121                                            iAudioResamplers, inputIAudioSamples, outputIAudioSamples,
122                                            inputIStreamCoder, outputIStreamCoders, _outputIContainer,
123                                            outputIStreams, inputICodecType, _outputURL, channels, rate,
124                                            i);
125                            }
126                            else if (inputICodecType == ICodec.Type.CODEC_TYPE_VIDEO) {
127                                    IStream outputIStream = _outputIContainer.addNewStream(i);
128    
129                                    outputIStreams[i] = outputIStream;
130    
131                                    IStreamCoder outputIStreamCoder =
132                                            outputIStream.getStreamCoder();
133    
134                                    outputIStreamCoders[i] = outputIStreamCoder;
135    
136                                    if (inputIStreamCoder.getBitRate() == 0) {
137                                            outputIStreamCoder.setBitRate(250000);
138                                    }
139                                    else {
140                                            outputIStreamCoder.setBitRate(
141                                                    inputIStreamCoder.getBitRate());
142                                    }
143    
144                                    ICodec iCodec = ICodec.guessEncodingCodec(
145                                            null, null, _outputURL, null, inputICodecType);
146    
147                                    if (iCodec == null) {
148                                            throw new RuntimeException(
149                                                    "Unable to determine " + inputICodecType +
150                                                            " encoder for " + _outputURL);
151                                    }
152    
153                                    outputIStreamCoder.setCodec(iCodec);
154    
155                                    if ((inputIStreamCoder.getHeight() <= 0)) {
156                                            throw new RuntimeException(
157                                                    "Unable to determine height for " + _inputURL);
158                                    }
159    
160                                    outputIStreamCoder.setHeight(_height);
161    
162                                    IRational iRational = inputIStreamCoder.getFrameRate();
163    
164                                    outputIStreamCoder.setFrameRate(iRational);
165    
166                                    outputIStreamCoder.setPixelType(Type.YUV420P);
167                                    outputIStreamCoder.setTimeBase(
168                                            IRational.make(
169                                                    iRational.getDenominator(), iRational.getNumerator()));
170    
171                                    if (inputIStreamCoder.getWidth() <= 0) {
172                                            throw new RuntimeException(
173                                                    "Unable to determine width for " + _inputURL);
174                                    }
175    
176                                    outputIStreamCoder.setWidth(_width);
177    
178                                    iVideoResamplers[i] = createIVideoResampler(
179                                            inputIStreamCoder, outputIStreamCoder, _height, _width);
180    
181                                    inputIVideoPictures[i] = IVideoPicture.make(
182                                            inputIStreamCoder.getPixelType(),
183                                            inputIStreamCoder.getWidth(),
184                                            inputIStreamCoder.getHeight());
185                                    outputIVideoPictures[i] = IVideoPicture.make(
186                                            outputIStreamCoder.getPixelType(),
187                                            outputIStreamCoder.getWidth(),
188                                            outputIStreamCoder.getHeight());
189                            }
190    
191                            openStreamCoder(inputIStreamCoders[i]);
192                            openStreamCoder(outputIStreamCoders[i]);
193                    }
194    
195                    if (_outputIContainer.writeHeader() < 0) {
196                            throw new RuntimeException("Unable to write container header");
197                    }
198    
199                    boolean keyPacketFound = false;
200                    int nonKeyAfterKeyCount = 0;
201                    boolean onlyDecodeKeyPackets = false;
202                    int previousPacketSize = -1;
203    
204                    IPacket inputIPacket = IPacket.make();
205                    IPacket outputIPacket = IPacket.make();
206    
207                    while (_inputIContainer.readNextPacket(inputIPacket) == 0) {
208                            if (_log.isDebugEnabled()) {
209                                    _log.debug("Current packet size " + inputIPacket.getSize());
210                            }
211    
212                            int streamIndex = inputIPacket.getStreamIndex();
213    
214                            IStreamCoder inputIStreamCoder = inputIStreamCoders[streamIndex];
215                            IStreamCoder outputIStreamCoder = outputIStreamCoders[streamIndex];
216    
217                            if (outputIStreamCoder == null) {
218                                    continue;
219                            }
220    
221                            IStream iStream = _inputIContainer.getStream(streamIndex);
222    
223                            long timeStampOffset = getStreamTimeStampOffset(iStream);
224    
225                            if (inputIStreamCoder.getCodecType() ==
226                                            ICodec.Type.CODEC_TYPE_AUDIO) {
227    
228                                    decodeAudio(
229                                            iAudioResamplers[streamIndex],
230                                            inputIAudioSamples[streamIndex],
231                                            outputIAudioSamples[streamIndex], inputIPacket,
232                                            outputIPacket, inputIStreamCoder, outputIStreamCoder,
233                                            _outputIContainer, inputIPacket.getSize(),
234                                            previousPacketSize, streamIndex, timeStampOffset);
235                            }
236                            else if (inputIStreamCoder.getCodecType() ==
237                                                    ICodec.Type.CODEC_TYPE_VIDEO) {
238    
239                                    keyPacketFound = isKeyPacketFound(inputIPacket, keyPacketFound);
240    
241                                    nonKeyAfterKeyCount = countNonKeyAfterKey(
242                                            inputIPacket, keyPacketFound, nonKeyAfterKeyCount);
243    
244                                    if (isStartDecoding(
245                                                    inputIPacket, inputIStreamCoder, keyPacketFound,
246                                                    nonKeyAfterKeyCount, onlyDecodeKeyPackets)) {
247    
248                                            int value = decodeVideo(
249                                                    iVideoResamplers[streamIndex],
250                                                    inputIVideoPictures[streamIndex],
251                                                    outputIVideoPictures[streamIndex], inputIPacket,
252                                                    outputIPacket, inputIStreamCoder, outputIStreamCoder,
253                                                    _outputIContainer, null, null, 0, 0, timeStampOffset);
254    
255                                            if (value <= 0) {
256                                                    if (inputIPacket.isKey()) {
257                                                            throw new RuntimeException(
258                                                                    "Unable to decode video stream " + streamIndex);
259                                                    }
260    
261                                                    onlyDecodeKeyPackets = true;
262    
263                                                    continue;
264                                            }
265                                    }
266                                    else {
267                                            if (_log.isDebugEnabled()) {
268                                                    _log.debug("Do not decode video stream " + streamIndex);
269                                            }
270                                    }
271                            }
272    
273                            previousPacketSize = inputIPacket.getSize();
274                    }
275    
276                    flush(outputIStreamCoders, _outputIContainer);
277    
278                    if (_outputIContainer.writeTrailer() < 0) {
279                            throw new RuntimeException(
280                                    "Unable to write trailer to output file");
281                    }
282    
283                    cleanUp(inputIStreamCoders, outputIStreamCoders);
284            }
285    
286            @Override
287            protected IContainer getInputIContainer() {
288                    return _inputIContainer;
289            }
290    
291            private static Log _log = LogFactoryUtil.getLog(
292                    LiferayVideoConverter.class);
293    
294            private int _channels;
295            private int _height = 240;
296            private IContainer _inputIContainer;
297            private String _inputURL;
298            private IContainer _outputIContainer;
299            private String _outputURL;
300            private int _rate;
301            private int _width = 320;
302    
303    }