001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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            @Override
049            public void convert() throws Exception {
050                    try {
051                            doConvert();
052                    }
053                    finally {
054                            if (_inputIContainer.isOpened()) {
055                                    _inputIContainer.close();
056                            }
057                    }
058            }
059    
060            protected void doConvert() throws Exception {
061                    _inputIContainer = IContainer.make();
062    
063                    openContainer(_inputIContainer, _inputURL, false);
064    
065                    long seekTimeStamp = -1;
066    
067                    if ((_percentage > 0) && (_percentage <= 100)) {
068                            seekTimeStamp = getSeekTimeStamp(_percentage);
069                    }
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                    IVideoPicture[] inputIVideoPictures =
078                            new IVideoPicture[inputStreamsCount];
079    
080                    IStreamCoder[] inputIStreamCoders = new IStreamCoder[inputStreamsCount];
081    
082                    for (int i = 0; i < inputStreamsCount; i++) {
083                            IStream inputIStream = _inputIContainer.getStream(i);
084    
085                            IStreamCoder inputIStreamCoder = inputIStream.getStreamCoder();
086    
087                            inputIStreamCoders[i] = inputIStreamCoder;
088    
089                            if (inputIStreamCoder.getCodecType() ==
090                                            ICodec.Type.CODEC_TYPE_VIDEO) {
091    
092                                    inputIVideoPictures[i] = IVideoPicture.make(
093                                            inputIStreamCoder.getPixelType(),
094                                            inputIStreamCoder.getWidth(),
095                                            inputIStreamCoder.getHeight());
096                            }
097    
098                            openStreamCoder(inputIStreamCoder);
099                    }
100    
101                    boolean thumbnailGenerated = false;
102    
103                    try {
104                            if (seekTimeStamp != -1) {
105                                    rewind();
106    
107                                    seek(seekTimeStamp);
108                            }
109    
110                            thumbnailGenerated = generateThumbnail(
111                                    inputIStreamCoders, inputIVideoPictures);
112                    }
113                    catch (Exception e) {
114                    }
115    
116                    if (!thumbnailGenerated) {
117                            if (_log.isWarnEnabled()) {
118                                    _log.warn(
119                                            "Unable to create thumbnail from specified frame. Will " +
120                                                    "generate thumbnail from the beginning.");
121                            }
122    
123                            rewind();
124    
125                            generateThumbnail(inputIStreamCoders, inputIVideoPictures);
126                    }
127    
128                    cleanUp(inputIVideoPictures, null);
129                    cleanUp(inputIStreamCoders, null);
130            }
131    
132            protected boolean generateThumbnail(
133                            IStreamCoder[] inputIStreamCoders,
134                            IVideoPicture[] inputIVideoPictures)
135                    throws Exception {
136    
137                    boolean keyPacketFound = false;
138                    int nonKeyAfterKeyCount = 0;
139                    boolean onlyDecodeKeyPackets = false;
140    
141                    IPacket inputIPacket = IPacket.make();
142    
143                    while (_inputIContainer.readNextPacket(inputIPacket) == 0) {
144                            if (_log.isDebugEnabled()) {
145                                    _log.debug("Current packet size " + inputIPacket.getSize());
146                            }
147    
148                            int streamIndex = inputIPacket.getStreamIndex();
149    
150                            IStreamCoder inputIStreamCoder = inputIStreamCoders[streamIndex];
151    
152                            if (inputIStreamCoder.getCodecType() !=
153                                            ICodec.Type.CODEC_TYPE_VIDEO) {
154    
155                                    continue;
156                            }
157    
158                            keyPacketFound = isKeyPacketFound(inputIPacket, keyPacketFound);
159    
160                            nonKeyAfterKeyCount = countNonKeyAfterKey(
161                                    inputIPacket, keyPacketFound, nonKeyAfterKeyCount);
162    
163                            if (isStartDecoding(
164                                            inputIPacket, inputIStreamCoder, keyPacketFound,
165                                            nonKeyAfterKeyCount, onlyDecodeKeyPackets)) {
166    
167                                    IStream iStream = _inputIContainer.getStream(streamIndex);
168    
169                                    long timeStampOffset = getStreamTimeStampOffset(iStream);
170    
171                                    int value = decodeVideo(
172                                            null, inputIVideoPictures[streamIndex], null, inputIPacket,
173                                            null, inputIStreamCoder, null, null, _outputFile,
174                                            _extension, _height, _width, timeStampOffset);
175    
176                                    if (value <= 0) {
177                                            if (inputIPacket.isKey()) {
178                                                    throw new RuntimeException(
179                                                            "Unable to decode video stream " + streamIndex);
180                                            }
181    
182                                            onlyDecodeKeyPackets = true;
183    
184                                            continue;
185                                    }
186                                    else if (value == DECODE_VIDEO_THUMBNAIL) {
187                                            cleanUp(inputIPacket, null);
188    
189                                            return true;
190                                    }
191                            }
192                            else {
193                                    if (_log.isDebugEnabled()) {
194                                            _log.debug("Do not decode video stream " + streamIndex);
195                                    }
196                            }
197                    }
198    
199                    cleanUp(inputIPacket, null);
200    
201                    return false;
202            }
203    
204            @Override
205            protected IContainer getInputIContainer() {
206                    return _inputIContainer;
207            }
208    
209            private static Log _log = LogFactoryUtil.getLog(
210                    LiferayVideoThumbnailConverter.class);
211    
212            private String _extension;
213            private int _height = 240;
214            private IContainer _inputIContainer;
215            private String _inputURL;
216            private File _outputFile;
217            private int _percentage;
218            private int _width = 320;
219    
220    }