Alfresco has undergone quite a few changes under the hood in version 3.3, including refactoring of the ThumbnailService to make use of the RenditionService, which I had to explore in a recent deep dive to get video working for the thumbnails extension. This is obviously a subject dear to your heart as well or you would have moved on by now.
Alfresco 3.1.
Let’s take a look at how things worked back in the old days, before 3.2 (fuzzy 2009 dream sequence begins…)
You most likely kicked things off with a thumbnail generating URL which would hit a ScriptNode.createThumbnail
method which looked up the thumbnailDefinition
and called the ThumbnailService
with the TransformationOptions
contained in the definition.
The ThumbnailService
then told the ContentService
to perform the transformation which looked up the ContentTransformer
from the TransformerRegistry
and executed transformer.transform
which usually delegated to a RuntimeExec
object to perform the actual command line transformation.
If we were to visualize the key elements of that stack for a synchronous video thumbnail creation it would look something like:
- org.alfresco.repo.jscript.ScriptNode.createThumbnail
- org.alfresco.repo.thumbnail.ThumbnailRegistry.getThumbnailDefinition (loads TransformationOptions)
- org.alfresco.repo.thumbnail.ThumbnailServiceImpl.createThumbnail
- org.alfresco.repo.content.ContentServiceImpl.transform
- org.alfresco.repo.content.transform.ContentTransformerRegistry.getTransformer
- org.alfresco.repo.content.transform.ContentTransformer.transform
- TempFileProvider.createTempFile
- org.alfresco.util.exec.RuntimeExec.execute
- ffmpeg …
- org.alfresco.repo.content.ContentServiceImpl.transform
Well now, that’s not so bad.
Alfresco 3.3.
These Alfresco people don’t mess around. If it’s determined that refactoring should happen, it happens, real damn quick, even if it’s huge. To that end subsystems were rolled out in 3.2 and the RenditionService was introduced in 3.3.
Subsystems. Certain content transformers, like ImageMagick, have been refactored as a subsystem, which is cool. Read the wiki page to get the full story but in short it’s a much more flexible architecture.
The ffmpeg video transformer contained in the latest release of the thumbnails extension follows this precedence and has been refactored as a subsystem, so there.
RenditionService. I wouldn’t say I hate it, but this package should be wary of eating crackers in bed, or maybe I’m just not familiar enough with it yet.
The new rendition service utilizes RenditionDefinitions
which require that any TransformationOptions
be ‘serialized’ into a parameter map, which are then reassembled into TransformationOptions
objects before being passed to the ContentTransformer
. I suppose this is done to make it easier to do things like hand renditions off to another server in a cluster, but it’s a bit of a pain in the ass for developers.
So, back to our synchronous video thumbnail creation scenario (deep breath), the ThumbnailService
now creates a RenditionDefinition
which it passes to the RenditionService
which wraps things up in a PerformRenditionActionExecuter
that gets passed to the ActionService
. The PerformRenditionActionExecuter
calls the ActionService
again which loads the RenderingEngine
from the spring ApplicationContext
. A RenderingEngine
is itself an ActionExecuter
so the ActionService
calls execute
which calls render
which proceeds to rebuild the TransformationOptions
object needed by the ContentService
to get the proper ContentTransformer
so transformer.transform
can use a RuntimeExec
object to perform the actual command line transformation.
The key elements of our pseudo stack/method trace would look like:
- org.alfresco.repo.jscript.ScriptNode.createThumbnail
- org.alfresco.repo.thumbnail.ThumbnailRegistry.getThumbnailDefinition (loads TransformationOptions)
- org.alfresco.repo.thumbnail.ThumbnailServiceImpl.createThumbnail
- …
- org.alfresco.repo.thumbnail.ThumbnailServiceImpl.createRenditionDefinition
- org.alfresco.repo.thumbnail.ThumbnailRegistry.getThumbnailRenditionConvertor().convert(transformationOptions, assocDetails) <- this serializes the transformationOptions into a parameter map
- org.alfresco.repo.rendition.RenditionServiceImpl.render
- org.alfresco.repo.rendition.RenditionServiceImpl.createAndExecuteRenditionAction
- org.alfresco.repo.action.ActionServiceImpl.createAction(PerformRenditionActionExecuter.NAME)
- set the renditionDefintion on performRenditionAction
- org.alfresco.repo.action.ActionServiceImpl.executeAction
- …
- org.alfresco.repo.action.ActionServiceImpl.directActionExecution
- load ActionExecuter from applicationContext
- org.alfresco.repo.rendition.executer.ImageRenderingEngine.execute
- …
- org.alfresco.repo.rendition.executer.ImageRenderingEngine.render
- …
- org.alfresco.repo.rendition.executer.ImageRenderingEngine.getTransformationOptions <- rebuilds parameter map into TransformationOptions objects
- org.alfresco.repo.content.ContentServiceImpl.transform
- org.alfresco.repo.content.transform.ContentTransformerRegistry.getTransformer
- org.alfresco.repo.content.transform.ProxyContentTransformer.transform <- subsystems
- org.alfresco.repo.content.transform.ContentTransformerWorker.transform
- TempFileProvider.createTempFile
- org.alfresco.util.exec.RuntimeExec.execute
- ffmpeg …
- org.alfresco.repo.content.transform.ContentTransformerWorker.transform
- org.alfresco.repo.rendition.RenditionServiceImpl.createAndExecuteRenditionAction
that’s a spicy meatball!
A while ago we submitted an issue to Alfresco suggesting TransformationOptions which contain distinct target AND source options so that one could for example specify that all ‘doclib’ thumbnails be of max-width X and max-height Y (the type of target options currently available) and furthermore, if it’s a document take page 1 (or maybe you need a different page in your deployment) or if it’s a video take 1 frame starting 0.5 seconds in, or if it’s a layered image file take the second layer, etc. (a list of source options).
Alas, this concept of SourceTargetTransformationOptions
hasn’t yet been embraced by the Alfresco team but is used by the video transformer in the thumbnails extension which made refactoring for renditions even more difficult, but you’re not here to listen to me bitch and moan, so I’ll just say that it’s done and seems to be working fine.
So, there you have it, renditions and subsystems as they relate to video thumbnails. Hit me up with any questions or things I’ve missed.
I’m trying to reconcile what you wrote with what I’ve seen in the
thumbnail definition context file. From looking at the context file
it looks as though I can only create a single “doclib” thumbnail
definition and that definition uses a single transformer (I’m not talking about complex transformations in this case). doclib was the one that was defined
OOTB and it uses Imagemagick. It seemed like I could not then create
a doclib thumbnail definition that also used FFmpeg for video since it
would be a duplicate definition of doclib. If this was true, then in
Share document library I was going to have to modify the javascript to
pick up a separate thumbnail type (e.g., a newly defined videothumnail) if
the mimetype was video. Anyway, I was pleasantly surprised that
thumbnails just magically appeared in Share with no additional work on
my part once the Thumbnail Extension was installed. Is that what you were referring to regarding the need for SourceTargetTranformation and is this what you coded around in the thumbnail extension?
You’re exactly right Steve.
The thumbnails extension replaces the built-in doclib and imgpreview thumbnail definitions with definitions that call on the SourceTargetImageTransformationOptions which allows for specifying source options for each source mimetype. The transformer itself isn’t specified in the thumbnail definition, just the source options, then at thumbnail creation time the transformers are polled to see if they know how to handle the options and source and target mimetypes.
With those SourceTargetTransformationOptions in place the standard thumbnail creation mechanisms can be used for generation including the thumbnail webscript URL that Share uses to create the thumbnails.