001/* 002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Thomas Roger 016 * Florent Guillaume 017 */ 018package org.nuxeo.ecm.platform.video.service; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import java.util.concurrent.TimeUnit; 028 029import org.apache.commons.logging.Log; 030import org.apache.commons.logging.LogFactory; 031import org.nuxeo.ecm.core.api.DocumentLocation; 032import org.nuxeo.ecm.core.api.DocumentModel; 033import org.nuxeo.ecm.core.api.NuxeoException; 034import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 035import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolder; 036import org.nuxeo.ecm.core.convert.api.ConversionService; 037import org.nuxeo.ecm.core.work.api.Work; 038import org.nuxeo.ecm.core.work.api.Work.State; 039import org.nuxeo.ecm.core.work.api.WorkManager; 040import org.nuxeo.ecm.core.work.api.WorkManager.Scheduling; 041import org.nuxeo.ecm.platform.video.TranscodedVideo; 042import org.nuxeo.ecm.platform.video.Video; 043import org.nuxeo.ecm.platform.video.VideoConversionStatus; 044import org.nuxeo.ecm.platform.video.VideoHelper; 045import org.nuxeo.ecm.platform.video.VideoInfo; 046import org.nuxeo.runtime.api.Framework; 047import org.nuxeo.runtime.model.ComponentContext; 048import org.nuxeo.runtime.model.ComponentInstance; 049import org.nuxeo.runtime.model.DefaultComponent; 050 051import static org.nuxeo.ecm.platform.video.service.Configuration.DEFAULT_CONFIGURATION; 052 053/** 054 * Default implementation of {@link VideoService}. 055 * 056 * @since 5.5 057 */ 058public class VideoServiceImpl extends DefaultComponent implements VideoService { 059 060 protected static final Log log = LogFactory.getLog(VideoServiceImpl.class); 061 062 public static final String VIDEO_CONVERSIONS_EP = "videoConversions"; 063 064 public static final String DEFAULT_VIDEO_CONVERSIONS_EP = "automaticVideoConversions"; 065 066 /** 067 * @since 7.4 068 */ 069 public static final String CONFIGURATION_EP = "configuration"; 070 071 protected VideoConversionContributionHandler videoConversions; 072 073 protected AutomaticVideoConversionContributionHandler automaticVideoConversions; 074 075 /** 076 * @since 7.4 077 */ 078 protected Configuration configuration = DEFAULT_CONFIGURATION; 079 080 @Override 081 public void activate(ComponentContext context) { 082 videoConversions = new VideoConversionContributionHandler(); 083 automaticVideoConversions = new AutomaticVideoConversionContributionHandler(); 084 } 085 086 @Override 087 public void deactivate(ComponentContext context) { 088 WorkManager workManager = Framework.getLocalService(WorkManager.class); 089 if (workManager != null && workManager.isStarted()) { 090 try { 091 workManager.shutdownQueue( 092 workManager.getCategoryQueueId(VideoConversionWork.CATEGORY_VIDEO_CONVERSION), 10, 093 TimeUnit.SECONDS); 094 } catch (InterruptedException e) { 095 // restore interrupted status 096 Thread.currentThread().interrupt(); 097 throw new RuntimeException(e); 098 } 099 } 100 videoConversions = null; 101 automaticVideoConversions = null; 102 } 103 104 @Override 105 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 106 switch (extensionPoint) { 107 case VIDEO_CONVERSIONS_EP: 108 videoConversions.addContribution((VideoConversion) contribution); 109 break; 110 case DEFAULT_VIDEO_CONVERSIONS_EP: 111 automaticVideoConversions.addContribution((AutomaticVideoConversion) contribution); 112 break; 113 case CONFIGURATION_EP: 114 configuration = (Configuration) contribution; 115 break; 116 } 117 } 118 119 @Override 120 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 121 switch (extensionPoint) { 122 case VIDEO_CONVERSIONS_EP: 123 videoConversions.removeContribution((VideoConversion) contribution); 124 break; 125 case DEFAULT_VIDEO_CONVERSIONS_EP: 126 automaticVideoConversions.removeContribution((AutomaticVideoConversion) contribution); 127 break; 128 case CONFIGURATION_EP: 129 configuration = DEFAULT_CONFIGURATION; 130 break; 131 } 132 } 133 134 @Override 135 public Collection<VideoConversion> getAvailableVideoConversions() { 136 return videoConversions.registry.values(); 137 } 138 139 @Override 140 public void launchConversion(DocumentModel doc, String conversionName) { 141 WorkManager workManager = Framework.getLocalService(WorkManager.class); 142 if (workManager == null) { 143 throw new RuntimeException("No WorkManager available"); 144 } 145 VideoConversionWork work = new VideoConversionWork(doc.getRepositoryName(), doc.getId(), conversionName); 146 workManager.schedule(work, Scheduling.IF_NOT_RUNNING_OR_SCHEDULED); 147 } 148 149 @Override 150 public void launchAutomaticConversions(DocumentModel doc) { 151 List<AutomaticVideoConversion> conversions = new ArrayList<>(automaticVideoConversions.registry.values()); 152 Collections.sort(conversions); 153 for (AutomaticVideoConversion conversion : conversions) { 154 launchConversion(doc, conversion.getName()); 155 } 156 } 157 158 @Override 159 @Deprecated 160 public TranscodedVideo convert(VideoConversionId id, Video originalVideo, String conversionName) { 161 return convert(originalVideo, conversionName); 162 } 163 164 @Override 165 public TranscodedVideo convert(Video originalVideo, String conversionName) { 166 if (!videoConversions.registry.containsKey(conversionName)) { 167 throw new NuxeoException(String.format("'%s' is not a registered video conversion.", conversionName)); 168 } 169 BlobHolder blobHolder = new SimpleBlobHolder(originalVideo.getBlob()); 170 VideoConversion conversion = videoConversions.registry.get(conversionName); 171 Map<String, Serializable> parameters = new HashMap<>(); 172 parameters.put("height", conversion.getHeight()); 173 parameters.put("videoInfo", originalVideo.getVideoInfo()); 174 ConversionService conversionService = Framework.getLocalService(ConversionService.class); 175 BlobHolder result = conversionService.convert(conversion.getConverter(), blobHolder, parameters); 176 VideoInfo videoInfo = VideoHelper.getVideoInfo(result.getBlob()); 177 return TranscodedVideo.fromBlobAndInfo(conversionName, result.getBlob(), videoInfo); 178 } 179 180 @Override 181 @Deprecated 182 public VideoConversionStatus getProgressStatus(VideoConversionId id) { 183 DocumentLocation loc = id.getDocumentLocation(); 184 return getProgressStatus(loc.getServerName(), loc.getIdRef().value, id.getConversionName()); 185 } 186 187 @Override 188 public VideoConversionStatus getProgressStatus(String repositoryName, String docId, String conversionName) { 189 WorkManager workManager = Framework.getLocalService(WorkManager.class); 190 Work work = new VideoConversionWork(repositoryName, docId, conversionName); 191 State state = workManager.getWorkState(work.getId()); 192 if (state == null || state == State.COMPLETED) { 193 return null; 194 } else if (state == State.SCHEDULED) { 195 String queueId = workManager.getCategoryQueueId(VideoConversionWork.CATEGORY_VIDEO_CONVERSION); 196 int queueSize = workManager.getQueueSize(queueId, State.SCHEDULED); 197 return new VideoConversionStatus(VideoConversionStatus.STATUS_CONVERSION_QUEUED, 0, queueSize); 198 } else { // RUNNING 199 return new VideoConversionStatus(VideoConversionStatus.STATUS_CONVERSION_PENDING, 0, 0); 200 } 201 } 202 203 @Override 204 public VideoConversion getVideoConversion(String conversionName) { 205 return videoConversions.registry.get(conversionName); 206 } 207 208 @Override 209 public Configuration getConfiguration() { 210 return configuration; 211 } 212}