001/* 002 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Ricardo Dias 018 */ 019package org.nuxeo.ecm.platform.video.tools; 020 021import org.apache.commons.io.FileUtils; 022import org.apache.commons.io.FilenameUtils; 023import org.apache.commons.lang.StringUtils; 024import org.nuxeo.ecm.automation.core.util.BlobList; 025import org.nuxeo.ecm.core.api.Blob; 026import org.nuxeo.ecm.core.api.NuxeoException; 027import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 028import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolder; 029import org.nuxeo.runtime.api.Framework; 030 031import java.io.File; 032import java.io.IOException; 033import java.nio.file.Path; 034import java.util.Map; 035 036/** 037 * The {@link VideoTool} for slicing video blobs. 038 * 039 * @since 8.4 040 */ 041public class VideoSlicer extends VideoTool { 042 043 public final static String NAME = "slicerTool"; 044 045 public final static String START_AT_PARAM = "startAt"; 046 047 public final static String DURATION_PARAM = "duration"; 048 049 public final static String ENCODE_PARAM = "encode"; 050 051 protected final static String SEGMENTS_PATH = "segmentsPath"; 052 053 protected static final String COMMAND_SLICER_DEFAULT = "videoSlicer"; 054 055 protected static final String COMMAND_SLICER_BY_COPY = "videoSlicerByCopy"; 056 057 protected static final String COMMAND_SLICER_SEGMENTS = "videoSlicerSegments"; 058 059 protected static final String COMMAND_SLICER_START_AT = "videoSlicerStartAt"; 060 061 public VideoSlicer() { 062 super(NAME, COMMAND_SLICER_DEFAULT); 063 } 064 065 @Override 066 public Map<String, String> setupParameters(BlobHolder blobHolder, Map<String, Object> parameters) { 067 Map<String, String> cmdParameters = super.setupParameters(blobHolder, parameters); 068 Blob video = blobHolder.getBlob(); 069 String startAt = (String) parameters.get(START_AT_PARAM); 070 String duration = (String) parameters.get(DURATION_PARAM); 071 boolean encode = (boolean) parameters.get(ENCODE_PARAM); 072 073 boolean useStart = !StringUtils.isEmpty(startAt); 074 boolean useDuration = !StringUtils.isEmpty(duration); 075 if (useStart) { 076 cmdParameters.put(START_AT_PARAM, startAt); 077 078 String finalFilename = setupFilename(video.getFilename(), startAt, duration); 079 File outputFile; 080 try { 081 outputFile = Framework.createTempFile(FilenameUtils.removeExtension(finalFilename), 082 "sliced." + FilenameUtils.getExtension(finalFilename)); 083 } catch (IOException e) { 084 throw new NuxeoException("VideoSlicer could not set up temporary output file.", e); 085 } 086 cmdParameters.put(OUTPUT_FILE_PATH_PARAM, outputFile.getAbsolutePath()); 087 088 // check if the slicer should use the duration 089 if (useDuration) { 090 cmdParameters.put(DURATION_PARAM, duration); 091 commandLineName = encode ? COMMAND_SLICER_DEFAULT : COMMAND_SLICER_BY_COPY; 092 } else { 093 commandLineName = COMMAND_SLICER_START_AT; 094 } 095 } else if (useDuration) { 096 Path segmentsDirectory; 097 try { 098 segmentsDirectory = Framework.createTempDirectory("Segments"); 099 } catch (IOException e) { 100 throw new NuxeoException("VideoSlicer could not create temporary directory for video segments", e); 101 } 102 File segments = segmentsDirectory.toFile(); 103 File dir = new File(segments.getAbsolutePath(), addSuffixToFileName(video.getFilename(), "-%03d")); 104 String outFilePattern = dir.getAbsolutePath(); 105 106 cmdParameters.put(OUTPUT_FILE_PATH_PARAM, outFilePattern); 107 cmdParameters.put(DURATION_PARAM, duration); 108 cmdParameters.put(SEGMENTS_PATH, segments.getAbsolutePath()); 109 commandLineName = COMMAND_SLICER_SEGMENTS; 110 } 111 return cmdParameters; 112 } 113 114 @Override 115 public BlobHolder buildResult(String mimeType, Map<String, String> cmdParams) { 116 if (!commandLineName.equals(COMMAND_SLICER_SEGMENTS)) { 117 return super.buildResult(mimeType, cmdParams); 118 } 119 // we have several blobs to build and track 120 BlobList parts = new BlobList(); 121 File segments = new File(cmdParams.get(SEGMENTS_PATH)); 122 mimeType = cmdParams.getOrDefault(OUTPUT_MIMETYPE_PARAM, mimeType); 123 for (File segmentFile : segments.listFiles()) { 124 Blob blob = getTemporaryBlob(segmentFile.getAbsolutePath(), mimeType); 125 parts.add(blob); 126 } 127 // now remove the temporary directory as the temporary FileBlobs were moved somewhere else 128 FileUtils.deleteQuietly(segments); 129 return new SimpleBlobHolder(parts); 130 } 131 132 private String setupFilename(String filename, String startAt, String duration) { 133 String suffix = StringUtils.isEmpty(startAt) ? "" : "-" + startAt.replaceAll(":", ""); 134 suffix += StringUtils.isEmpty(duration) ? "" : "-" + duration.replaceAll(":", ""); 135 return addSuffixToFileName(filename, suffix); 136 } 137 138 private String addSuffixToFileName(String filename, String suffix) { 139 if (StringUtils.isEmpty(filename) || StringUtils.isEmpty(suffix)) { 140 return filename; 141 } 142 int dotIndex = filename.lastIndexOf('.'); 143 if (dotIndex < 0) { 144 return filename + suffix; 145 } 146 return filename.substring(0, dotIndex) + suffix + filename.substring(dotIndex); 147 } 148}