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