001
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
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 }