All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.dam.video.FFMpegThumbnailProcess Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
package com.day.cq.dam.video;

import static com.day.cq.dam.api.DamConstants.THUMBNAIL_MIMETYPE;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jcr.RepositoryException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;

import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.Rendition;
import com.day.cq.dam.api.renditions.RenditionMaker;
import com.day.cq.dam.api.renditions.RenditionTemplate;
import com.day.cq.dam.api.thumbnail.ThumbnailConfig;
import com.day.cq.dam.commons.thumbnail.ThumbnailConfigImpl;
import com.day.cq.dam.handler.ffmpeg.FFMpegWrapper;
import com.day.cq.workflow.WorkflowSession;
import com.day.cq.workflow.metadata.MetaDataMap;
import com.day.image.Layer;

/**
 * Workflow process that calls FFMPEG on the command line to create thumbnails of the image. You can specify the
 * dimension of the thumbnails to be created
 * 

* For example, using the following workflow step arguments: *

 *    count:3,index:1,start:10,[140x100],[48x48]
 * 
*

* Will create thumbnails of size 140x100 and 48x48 with a black letterbox/pillarbox *

* This will only happen for assets having a video-based mime-type, others are ignored. * */ @Component(label = "Day CQ DAM FFmpeg Thumbnail Process", description = "Workflow process that creates thumbnails from video files") @Service @Properties({ @Property(name = "process.label", value = "Create Video Thumbnails", propertyPrivate=true) }) public class FFMpegThumbnailProcess extends AbstractFFMpegProcess{ @Reference private RenditionMaker renditionMaker; /** * The available arguments to this process implementation. */ public enum Arguments { PROCESS_ARGS("PROCESS_ARGS"), START("start"), COUNT("count"), INDEX("index"), CONFIGS("CONFIGS"); private String argumentName; Arguments(String argumentName) { this.argumentName = argumentName; } public String getArgumentName() { return this.argumentName; } public String getArgumentPrefix() { return this.argumentName + ":"; } } protected void processVideo(final MetaDataMap metaData, final Asset asset, final File tmpFile, final WorkflowSession wfSession) throws IOException, RepositoryException { String[] args = buildArguments(metaData); int start = 0, count = 1, index = 0; // parse out the start value int i = 0; while (i < args.length) { final String arg = args[i]; if (arg.startsWith("start:") || arg.startsWith("count:") || arg.startsWith("index:")) { final String[] split = StringUtils.split(arg, ":"); if (split.length == 2) { if (arg.startsWith("start:")) { start = NumberUtils.toInt(split[1], start); } else if (arg.startsWith("count:")) { count = NumberUtils.toInt(split[1], count); if (count <= 0) { count = 1; } } else if (arg.startsWith("index:")) { index = NumberUtils.toInt(split[1], index); } } // and remove the arg from the list of arguments // since incompatible with subsequent thumbnail config args = (String[]) ArrayUtils.remove(args, i); } else { i++; } } FFMpegWrapper wrapper=null; File tmpWorkingDir = null; File tmpRenditionFile = null; OutputStream out = null; InputStream in = null; try { //creating temp working directory for ffmpeg tmpWorkingDir = createTempDir(getWorkingDir()); wrapper = new FFMpegWrapper(tmpFile, tmpWorkingDir); wrapper.setExecutableLocator(locator); BufferedImage[] thumbnails = wrapper.getThumbnails(count, start); if (thumbnails != null && thumbnails.length > 0) { if (index >= thumbnails.length) { index = thumbnails.length - 1; } final BufferedImage thumbnail = thumbnails[index]; if (thumbnail != null) { final BufferedImage rgbaThumbnail = new BufferedImage(thumbnail.getWidth(), thumbnail.getHeight(), BufferedImage.TYPE_INT_ARGB); rgbaThumbnail.getGraphics().drawImage(thumbnail, 0, 0, null); // creating a temporary rendition through which normal renditions can be generated for an image. tmpRenditionFile = File.createTempFile("thumbnail", ".tmp"); Layer layer = new Layer(rgbaThumbnail); out = FileUtils.openOutputStream(tmpRenditionFile); layer.write(THUMBNAIL_MIMETYPE, 0.8, out); IOUtils.closeQuietly(out); in = FileUtils.openInputStream(tmpRenditionFile); final Rendition rendition = asset.addRendition("tempRendition", in, THUMBNAIL_MIMETYPE); ThumbnailConfig[] thumbConfigs = getThumbnailConfigs(args); // for each thumbnail config, create a rendition template RenditionTemplate[] templates = createRenditionTemplates(rendition, thumbConfigs, renditionMaker); // create thumbnail renditions renditionMaker.generateRenditions(asset, templates); //removing the temporary rendition asset.removeRendition(rendition.getName()); } } else { log.warn("Could not create thumbnails for video asset {}, maybe ffmpeg is not installed.", asset.getPath()); } } finally { try { IOUtils.closeQuietly(out); IOUtils.closeQuietly(in); // cleaning up ffmpeg's temp working directory if (tmpWorkingDir != null) { FileUtils.deleteDirectory(tmpWorkingDir); } // cleaning up temp file if(tmpRenditionFile != null) { FileUtils.deleteQuietly(tmpRenditionFile); } } catch (IOException e) { log.warn( "Could not delete ffmpeg's temporary working directory: {}", tmpWorkingDir.getPath()); } } } public String[] buildArguments(MetaDataMap metaData) { // the 'old' way, ensures backward compatibility String processArgs = metaData.get(Arguments.PROCESS_ARGS.name(), String.class); if (processArgs != null && !processArgs.equals("")) { return processArgs.split(","); } else { List arguments = new ArrayList(); String start = metaData.get(Arguments.START.name(), String.class); if(start != null){ StringBuilder builder = new StringBuilder(); builder.append(Arguments.START.getArgumentPrefix()).append(start); arguments.add(builder.toString()); } String count = metaData.get(Arguments.COUNT.name(), String.class); if(count != null){ StringBuilder builder = new StringBuilder(); builder.append(Arguments.COUNT.getArgumentPrefix()).append(count); arguments.add(builder.toString()); } String index = metaData.get(Arguments.INDEX.name(), String.class); if(index != null){ StringBuilder builder = new StringBuilder(); builder.append(Arguments.INDEX.getArgumentPrefix()).append(index); arguments.add(builder.toString()); } String[] configs = metaData.get(Arguments.CONFIGS.name(), String[].class); if (configs != null) { for(String config : configs){ arguments.add(config); } } return arguments.toArray(new String[arguments.size()]); } } private ThumbnailConfig[] getThumbnailConfigs(final String[] args) { final Set set = new HashSet(); for (String arg : args) { ThumbnailConfig config = null; // remove any whitespace arg = arg.trim(); // remove any square brackets arg = StringUtils.replaceEach(arg, new String[]{"[", "]"}, new String[]{"", ""}); final String fragments[] = arg.split(":"); // ensure sufficient arguments are present (at least width:height) if (fragments.length >= 2) { try { final Integer width = Integer.valueOf(fragments[0]); final Integer height = Integer.valueOf(fragments[1]); boolean doCenter = false; if (fragments.length > 2) { doCenter = Boolean.valueOf(fragments[2]); } config = new ThumbnailConfigImpl(width, height, doCenter); } catch (NumberFormatException e) { log.warn("parseConfig: cannot parse, invalid width/height specified in config [{}]: ", arg, e); } } else { log.warn("parseConfig: cannot parse, insufficient arguments in config [{}].", arg); } if(config != null) { set.add(config); } } return set.toArray(new ThumbnailConfig[]{}); } private RenditionTemplate[] createRenditionTemplates(Rendition rendition, ThumbnailConfig[] thumbnails, RenditionMaker renditionMaker) { RenditionTemplate[] templates = new RenditionTemplate[thumbnails.length]; for (int i = 0; i < thumbnails.length; i++) { ThumbnailConfig thumb = thumbnails[i]; templates[i] = renditionMaker.createThumbnailTemplate(rendition, thumb.getWidth(), thumb.getHeight(), thumb.doCenter()); } return templates; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy