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.ICodec;
021    import com.xuggle.xuggler.IContainer;
022    import com.xuggle.xuggler.IPacket;
023    import com.xuggle.xuggler.IStream;
024    import com.xuggle.xuggler.IStreamCoder;
025    import com.xuggle.xuggler.IVideoPicture;
026    
027    import java.io.File;
028    
029    /**
030     * @author Juan González
031     * @author Sergio González
032     * @author Brian Wing Shun Chan
033     */
034    public class LiferayVideoThumbnailConverter extends LiferayConverter {
035    
036            public LiferayVideoThumbnailConverter(
037                    String inputURL, File outputFile, String extension, int height,
038                    int width, int percentage) {
039    
040                    _inputURL = inputURL;
041                    _outputFile = outputFile;
042                    _extension = extension;
043                    _height = height;
044                    _width = width;
045                    _percentage = percentage;
046            }
047    
048            public void convert() throws Exception {
049                    try {
050                            doConvert();
051                    }
052                    finally {
053                            if (_inputIContainer.isOpened()) {
054                                    _inputIContainer.close();
055                            }
056                    }
057            }
058    
059            protected void doConvert() throws Exception {
060                    _inputIContainer = IContainer.make();
061    
062                    openContainer(_inputIContainer, _inputURL, false);
063    
064                    long seekTimeStamp = -1;
065    
066                    if ((_percentage > 0) && (_percentage <= 100)) {
067                            seekTimeStamp = getSeekTimeStamp(_percentage);
068                    }
069    
070                    int inputStreamsCount = _inputIContainer.getNumStreams();
071    
072                    if (inputStreamsCount < 0) {
073                            throw new RuntimeException("Input URL does not have any streams");
074                    }
075    
076                    IVideoPicture[] inputIVideoPictures =
077                            new IVideoPicture[inputStreamsCount];
078    
079                    IStreamCoder[] inputIStreamCoders = new IStreamCoder[inputStreamsCount];
080    
081                    for (int i = 0; i < inputStreamsCount; i++) {
082                            IStream inputIStream = _inputIContainer.getStream(i);
083    
084                            IStreamCoder inputIStreamCoder = inputIStream.getStreamCoder();
085    
086                            inputIStreamCoders[i] = inputIStreamCoder;
087    
088                            if (inputIStreamCoder.getCodecType() ==
089                                            ICodec.Type.CODEC_TYPE_VIDEO) {
090    
091                                    inputIVideoPictures[i] = IVideoPicture.make(
092                                            inputIStreamCoder.getPixelType(),
093                                            inputIStreamCoder.getWidth(),
094                                            inputIStreamCoder.getHeight());
095                            }
096    
097                            if ((inputIStreamCoder != null) && (inputIStreamCoder.open() < 0)) {
098                                    throw new RuntimeException("Unable to open input coder");
099                            }
100                    }
101    
102                    boolean thumbnailGenerated = false;
103    
104                    try {
105                            if (seekTimeStamp != -1) {
106                                    rewind();
107    
108                                    seek(seekTimeStamp);
109                            }
110    
111                            thumbnailGenerated = generateThumbnail(
112                                    inputIStreamCoders, inputIVideoPictures);
113                    }
114                    catch (Exception e) {
115                    }
116    
117                    if (!thumbnailGenerated) {
118                            if (_log.isWarnEnabled()) {
119                                    _log.warn(
120                                            "Unable to create thumbnail from specified frame. Will " +
121                                                    "generate thumbnail from the beginning.");
122                            }
123    
124                            rewind();
125    
126                            generateThumbnail(inputIStreamCoders, inputIVideoPictures);
127                    }
128            }
129    
130            protected boolean generateThumbnail(
131                            IStreamCoder[] inputIStreamCoders,
132                            IVideoPicture[] inputIVideoPictures)
133                    throws Exception {
134    
135                    boolean keyPacketFound = false;
136                    int nonKeyAfterKeyCount = 0;
137                    boolean onlyDecodeKeyPackets = false;
138    
139                    IPacket inputIPacket = IPacket.make();
140    
141                    while (_inputIContainer.readNextPacket(inputIPacket) == 0) {
142                            if (_log.isDebugEnabled()) {
143                                    _log.debug("Current packet size " + inputIPacket.getSize());
144                            }
145    
146                            int streamIndex = inputIPacket.getStreamIndex();
147    
148                            IStreamCoder inputIStreamCoder = inputIStreamCoders[streamIndex];
149    
150                            if (inputIStreamCoder.getCodecType() !=
151                                            ICodec.Type.CODEC_TYPE_VIDEO) {
152    
153                                    continue;
154                            }
155    
156                            keyPacketFound = isKeyPacketFound(inputIPacket, keyPacketFound);
157    
158                            nonKeyAfterKeyCount = countNonKeyAfterKey(
159                                    inputIPacket, keyPacketFound, nonKeyAfterKeyCount);
160    
161                            if (isStartDecoding(
162                                            inputIPacket, inputIStreamCoder, keyPacketFound,
163                                            nonKeyAfterKeyCount, onlyDecodeKeyPackets)) {
164    
165                                    IStream iStream = _inputIContainer.getStream(streamIndex);
166    
167                                    long timeStampOffset = getStreamTimeStampOffset(iStream);
168    
169                                    int value = decodeVideo(
170                                            null, inputIVideoPictures[streamIndex], null, inputIPacket,
171                                            null, inputIStreamCoder, null, null, _outputFile,
172                                            _extension, _height, _width, timeStampOffset);
173    
174                                    if (value <= 0) {
175                                            if (inputIPacket.isKey()) {
176                                                    throw new RuntimeException(
177                                                            "Unable to decode video stream " + streamIndex);
178                                            }
179    
180                                            onlyDecodeKeyPackets = true;
181    
182                                            continue;
183                                    }
184                                    else if (value == DECODE_VIDEO_THUMBNAIL) {
185                                            return true;
186                                    }
187                            }
188                            else {
189                                    if (_log.isDebugEnabled()) {
190                                            _log.debug("Do not decode video stream " + streamIndex);
191                                    }
192                            }
193                    }
194    
195                    return false;
196            }
197    
198            @Override
199            protected IContainer getInputIContainer() {
200                    return _inputIContainer;
201            }
202    
203            private static Log _log = LogFactoryUtil.getLog(
204                    LiferayVideoThumbnailConverter.class);
205    
206            private String _extension;
207            private int _height = 240;
208            private IContainer _inputIContainer;
209            private String _inputURL;
210            private File _outputFile;
211            private int _percentage;
212            private int _width = 320;
213    
214    }