Yesterday, Philip briefly wrote about Tumbler, the new D-Bus thumbnailing service that is going to be used on Xfce 4.8 and Maemo 6. Today, I'd like to explain how to use this service in client applications. Depending on the application type, the usage varies a little, so I'll focus on the basics here. I'll discuss some tricks at the end of this post though. What I'm not going to do is to talk about how to connect to D-Bus, how to call methods on D-Bus objects and so on. See the D-Bus tutorial or toolkit-specific documentation for more information about that.
A separate post with information on how to extend the thumbnail service will hopefull follow soon.
The Service Architecture
The thumbnailer specification defines four D-Bus service APIs. For most client applications only two of these are interesting:
org.freedesktop.thumbnails.Thumbnailer1 (the thumbnailing service) and
org.freedesktop.thumbnails.Cache1 (the thumbnail cache manager). The thumbnailing service can be used to request thumbnails and get feedback on the progress of requests. The cache manager can be used to keep the cache synchronized with the hard drive contents by notifying it when files are deleted, copied or renamed.
Thumbnail Request Workflow
Thumbnail requests include the following information:
- An array of URIs for which thumbnails should be generated
- An array of MIME types for these URIs (each element corresponding to the URI with the same index in the URI array)
- The thumbnail flavor to generate for the URIs (
normal is 128x128px,
large is 256x256px, ...)
- The scheduling mechanism to be used for the request (foreground, background, ...)
- An optional handle of a previous request that should now be cancelled
Because the service implementation might vary depending on the system (right now there's only Tumbler, but who knows about the future...) there is no fixed set of thumbnail flavors and schedulers. The specification is supposed to define a standard set of flavors and schedulers that all implementations have to support (like a
normal flavor and the scheduler
default). The URI schemes and MIME types supported by the thumbnailing service also depend on the implementation and the availability of thumbnailer plugins and applications extendending the thumbnailing service via D-Bus. So, the first thing an application should do is to to determine which flavors, schedulers, URI schemes and MIME types are supported.
Determine flavors, schedulers, URI schemes and MIME types supported by the service
The thumbnailer service provides the methods
GetSupported for this. They all return string arrays with supported flavors, schedulers and URI scheme and MIME type pairs.
GetSupported is a bit special, as it returns two arrays (one with URI schemes, the other with MIME types). These are to be interpreted as arrays of scheme/type pairs, each pair of which means that files with both the scheme and the MIME type at a certain index are supported. Applications can use this information to avoid requests for unsupported files. Once all this information is collected, we can continue with...
Let us assume for a moment that only one thumbnail is needed and that the flavor we want is
normal, the scheduler is
foreground, the URI is
file:///tmp/foo.png and the MIME type obviously is
image/png. We can request a thumbnail to be created for this URI by calling the
Queue method of
...thumbnails.Thumbnailer1 like this (written in some kind of fantasy language with dynamic D-Bus bindings ;)):
handle = service.Queue ("file:///tmp/foo.png", "image/png", "normal", "foreground", 0)
As you can see here, requesting thumbnails for more than this one URI is simple: just append one more URI to the first array and one more MIME type to the second array.
If you perhaps need to cancel the request later, remember the handle returned by the service. In complex applications with asynchronous APIs you'll sometimes need to link internal request handle to thumbnailer service handles, so the handle returned by the service is not only useful for canceling requests.
Being Notified About the Progress of a Thumbnail Request
Once you have requested one or more thumbnails, you'll probably want to be notified about the progress of your request. Tumbler will emit D-Bus signals for the following status updates:
Started is emitted together with the request handle as soon as the service starts processing the URIs of the request.
Ready is emitted together with the request handle and an array of URIs whenever the thumbnail for one or more of the URIs of the request were generated and can now be used. The array passed to you contains the source URIs, not the URIs of the thumbnail files.
Error is emitted together with the request handle, an array of URIs, an error message and an error code whenever the generation of a thumbnail for one or more URIs of the request failed.
Finished is emitted together with the request handle once the thumbnail service has finished processing the request. This can happen when all URIs have been looked at or when the request was cancelled.
Please note that there is no guarantee for the
Error signal to be emitted for all URIs of the request if the request is cancelled. So, if you maintain an internal thumbnail state for URIs depending on the thumbnail progress, you'll have to remember which URIs were queued. In any case you're advised to make sure to free up your own resources in case the service dies or D-Bus crashes. This can be achieved using a timeout (which will also be helpful if the service hangs) or by connecting to the
destroy signal of the
DBusGProxy (or whatever equivalent D-Bus API is used in your application).
If you want to cancel certain requests, all you need to do is to call the
Dequeue method of the service and pass the correct request handle to it.
Loading the Thumbnails
Where to look for the actual thumbnail files after they have been generated depends on the platform. If Tumbler is built with the default cache backend (which should be built on normal desktop systems), files are stored according to the thumbnail managing standard. On Maemo, files are stored as JPEGs. Philip might be able to give a more detailed description about how to access them.
Thumbnail Cache Synchronization and Cleanup
Some applications like file managers or image viewers allow users to rename, copy, move or delete files on the disk. Since renaming, copying and moving doesn't affect the contents of a file, you can avoid to regenerate its thumbnail by notifying the thumbnail cache of this change. When a file is deleted the thumbnail is no longer needed, so in order to prevent the cache from being polluted with dead thumbnails, you can notify it as well.
This is what the
...thumbnails.Cache1 service interface is for. It provides the following D-Bus methods for the above scenarios:
Copy (string array from_uris, string array to_uris)
Move (string array from_uris, string array to_uris) (this one is useful for both moving and renaming)
Delete (string array uris)
If you want to clean up the cache in a more general sense by deleting very old thumbnails you can do this via the following method which deletes all thumbnail whose original files have not been modified in a long time and have a certain URI prefix (which may be empty):
Cleanup (string uri_prefix, uint64 original_mtime_threshold)
The above should give you an insight into how the thumbnail service can be used in applications. If you have any questions about this, please let me know. Ok, now let's reveal some tricks to perform optimization on the client side.
Tips and Tricks
- Use asynchronous D-Bus calls in your application to avoid your application to block. A separate worker thread might also be useful.
- It's wise to cache the arrays returned from
SupportedChanged signal is planned to allow applications to update their cached information when the thumbnailer is extended by additional URI schemes or MIME types at runtime.
- If your application generates a lot of thumbnail requests, as file managers and picture viewers with gallery support usually do, you'll probably want to reduce the amount of messages being sent over D-Bus. You can do this by grouping individual requests. Often when you use a
GtkTreeView or something similar involving stateless cell renderers, the easiest way to generate thumbnail requests is when the cell renderer renders a file icon. If you don't group these individual requests, the system might go nuts. So, to group these requests you could use a worker thread with a wait queue that is flushed (URIs in it are sent to the thumbnail service as a single request) at most every X milliseconds. When the first thumbnail is needed, you start a one-shot timeout handler that is executed after X milliseconds, grouping all URIs added to the wait queue in the meantime. I do this in Thunar and it has proven to work well. It also scales nicely by grouping more efficiently the more thumbnails are needed in the time slot. Heavier user scrolling won't make things worse.
I guess that's it for now. I hope you enjoyed reading this first real post about Tumbler coming from me (which is also pushed to Planet Maemo by the way). Again, if you have any questions, please let me know.