Mercurial > hg > orthanc
changeset 5426:c65e036d649b
StorageCache is now storing transcoded instances + added ?transcode=... option to the /file route.
author | Alain Mazy <am@osimis.io> |
---|---|
date | Thu, 16 Nov 2023 16:09:04 +0100 |
parents | e2c9f9d9700e |
children | 111e21b4f8bc |
files | NEWS OrthancFramework/Sources/Cache/MemoryStringCache.cpp OrthancFramework/Sources/Cache/MemoryStringCache.h OrthancFramework/Sources/Enumerations.cpp OrthancFramework/Sources/Enumerations.h OrthancFramework/Sources/FileStorage/StorageCache.cpp OrthancFramework/Sources/FileStorage/StorageCache.h OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp OrthancServer/Sources/ServerContext.cpp OrthancServer/Sources/ServerContext.h |
diffstat | 11 files changed, 158 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Wed Nov 15 08:58:45 2023 +0100 +++ b/NEWS Thu Nov 16 16:09:04 2023 +0100 @@ -10,8 +10,10 @@ This can bring some significant improvements particularly in viewers. - Optimized the StorageCache to prevent loading the same file multiple times if multiple users request the same file at the same time. + - The StorageCache is now also storing transcoded instances that have been requested by /file?transcode=... + that is now used by the DICOMweb plugin. This speeds up retrieval of transcoded frames through WADO-RS. * Housekeeper plugin: - - Update to rebuild the cache of the DicomWeb plugin when updating to DicomWeb 1.15. + - Update to rebuild the cache of the DICOMweb plugin when updating to DICOMweb 1.15. - New trigger configuration: "DicomWebCacheChange" - Fixed reading the triggers configuration. * HTTP Compression: @@ -37,6 +39,8 @@ * API version upgraded to 22 * Added a route to delete completed jobs from history: DELETE /jobs/{id} +* added a "transcode" option to the /file route: + e.g: /instances/../file?transcode=1.2.840.10008.1.2.4.80 * All 'expand' GET arguments now accepts expand=true and expand=false values. The /studies/../instances and sibling routes are the only whose expand is true if not specified. These routes now accepts expand=false to simply list the child resources ids.
--- a/OrthancFramework/Sources/Cache/MemoryStringCache.cpp Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancFramework/Sources/Cache/MemoryStringCache.cpp Thu Nov 16 16:09:04 2023 +0100 @@ -206,6 +206,24 @@ } + void MemoryStringCache::InvalidateByPrefix(const std::string& keyPrefix) + { + std::vector<std::string> allKeys; + + { + boost::mutex::scoped_lock cacheLock(cacheMutex_); + content_.GetAllKeys(allKeys); + } + + for (std::vector<std::string>::const_iterator it = allKeys.begin(); it != allKeys.end(); ++it) + { + if (it->find(keyPrefix) == 0) + { + Invalidate(*it); + } + } + } + bool MemoryStringCache::Fetch(std::string& value, const std::string& key) {
--- a/OrthancFramework/Sources/Cache/MemoryStringCache.h Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancFramework/Sources/Cache/MemoryStringCache.h Thu Nov 16 16:09:04 2023 +0100 @@ -86,6 +86,7 @@ void Invalidate(const std::string& key); + void InvalidateByPrefix(const std::string& keyPrefix); private: void Add(const std::string& key,
--- a/OrthancFramework/Sources/Enumerations.cpp Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancFramework/Sources/Enumerations.cpp Thu Nov 16 16:09:04 2023 +0100 @@ -2468,6 +2468,21 @@ throw OrthancException(ErrorCode_InternalError); } } + + DicomTransferSyntax GetTransferSyntax(const std::string& value) + { + DicomTransferSyntax syntax; + if (LookupTransferSyntax(syntax, value)) + { + return syntax; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange, + "Unknown transfer syntax: " + value); + } + } + }
--- a/OrthancFramework/Sources/Enumerations.h Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancFramework/Sources/Enumerations.h Thu Nov 16 16:09:04 2023 +0100 @@ -935,6 +935,9 @@ const std::string& uid); ORTHANC_PUBLIC + DicomTransferSyntax GetTransferSyntax(const std::string& uid); + + ORTHANC_PUBLIC const char* GetResourceTypeText(ResourceType type, bool isPlural, bool isUpperCase);
--- a/OrthancFramework/Sources/FileStorage/StorageCache.cpp Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancFramework/Sources/FileStorage/StorageCache.cpp Thu Nov 16 16:09:04 2023 +0100 @@ -38,13 +38,22 @@ { return uuid + ":" + boost::lexical_cast<std::string>(contentType) + ":1"; } - + + static std::string GetCacheKeyStartRange(const std::string& uuid, FileContentType contentType) { return uuid + ":" + boost::lexical_cast<std::string>(contentType) + ":0"; } + + static std::string GetCacheKeyTranscodedInstance(const std::string& uuid, + DicomTransferSyntax transferSyntax) + { + return uuid + ":" + GetTransferSyntaxUid(transferSyntax) + ":1"; + } + + void StorageCache::SetMaximumSize(size_t size) { cache_.SetMaximumSize(size); @@ -54,12 +63,8 @@ void StorageCache::Invalidate(const std::string& uuid, FileContentType contentType) { - // invalidate both full file + start range file - const std::string keyFullFile = GetCacheKeyFullFile(uuid, contentType); - cache_.Invalidate(keyFullFile); - - const std::string keyPartialFile = GetCacheKeyStartRange(uuid, contentType); - cache_.Invalidate(keyPartialFile); + // invalidate full file, start range file and possible transcoded instances + cache_.InvalidateByPrefix(uuid); } @@ -111,6 +116,32 @@ } } + bool StorageCache::Accessor::FetchTranscodedInstance(std::string& value, + const std::string& uuid, + DicomTransferSyntax targetSyntax) + { + const std::string key = GetCacheKeyTranscodedInstance(uuid, targetSyntax); + if (MemoryStringCache::Accessor::Fetch(value, key)) + { + LOG(INFO) << "Read instance \"" << uuid << "\" transcoded to " + << GetTransferSyntaxUid(targetSyntax) << " from cache"; + return true; + } + else + { + return false; + } + } + + void StorageCache::Accessor::AddTranscodedInstance(const std::string& uuid, + DicomTransferSyntax targetSyntax, + const void* buffer, + size_t size) + { + const std::string key = GetCacheKeyTranscodedInstance(uuid, targetSyntax); + MemoryStringCache::Accessor::Add(key, reinterpret_cast<const char*>(buffer), size); + } + bool StorageCache::Accessor::FetchStartRange(std::string& value, const std::string& uuid, FileContentType contentType,
--- a/OrthancFramework/Sources/FileStorage/StorageCache.h Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancFramework/Sources/FileStorage/StorageCache.h Thu Nov 16 16:09:04 2023 +0100 @@ -50,26 +50,35 @@ Accessor(StorageCache& cache); void Add(const std::string& uuid, - FileContentType contentType, - const std::string& value); + FileContentType contentType, + const std::string& value); void AddStartRange(const std::string& uuid, - FileContentType contentType, - const std::string& value); + FileContentType contentType, + const std::string& value); void Add(const std::string& uuid, - FileContentType contentType, - const void* buffer, - size_t size); + FileContentType contentType, + const void* buffer, + size_t size); bool Fetch(std::string& value, - const std::string& uuid, - FileContentType contentType); + const std::string& uuid, + FileContentType contentType); bool FetchStartRange(std::string& value, - const std::string& uuid, - FileContentType contentType, - uint64_t end /* exclusive */); + const std::string& uuid, + FileContentType contentType, + uint64_t end /* exclusive */); + + bool FetchTranscodedInstance(std::string& value, + const std::string& uuid, + DicomTransferSyntax targetSyntax); + + void AddTranscodedInstance(const std::string& uuid, + DicomTransferSyntax targetSyntax, + const void* buffer, + size_t size); }; private:
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp Thu Nov 16 16:09:04 2023 +0100 @@ -93,21 +93,6 @@ } } - - static DicomTransferSyntax GetTransferSyntax(const std::string& value) - { - DicomTransferSyntax syntax; - if (LookupTransferSyntax(syntax, value)) - { - return syntax; - } - else - { - throw OrthancException(ErrorCode_ParameterOutOfRange, - "Unknown transfer syntax: " + value); - } - } - static void GetJobParameters(bool& synchronous, /* out */ bool& extended, /* out */ @@ -137,7 +122,7 @@ body.isMember(KEY_TRANSCODE)) { transcode = true; - syntax = GetTransferSyntax(SerializationToolbox::ReadString(body, KEY_TRANSCODE)); + syntax = Orthanc::GetTransferSyntax(SerializationToolbox::ReadString(body, KEY_TRANSCODE)); } else {
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Thu Nov 16 16:09:04 2023 +0100 @@ -361,6 +361,8 @@ static void GetInstanceFile(RestApiGetCall& call) { + static const char* const TRANSCODE = "transcode"; + if (call.IsDocumentation()) { call.GetDocumentation() @@ -369,6 +371,9 @@ .SetDescription("Download one DICOM instance") .SetUriArgument("id", "Orthanc identifier of the DICOM instance of interest") .SetHttpHeader("Accept", "This HTTP header can be set to retrieve the DICOM instance in DICOMweb format") + .SetHttpGetArgument(TRANSCODE, RestApiCallDocumentation::Type_String, + "If present, the DICOM file will be transcoded to the provided " + "transfer syntax: https://book.orthanc-server.com/faq/transcoding.html", false) .AddAnswerType(MimeType_Dicom, "The DICOM instance") .AddAnswerType(MimeType_DicomWebJson, "The DICOM instance, in DICOMweb JSON format") .AddAnswerType(MimeType_DicomWebXml, "The DICOM instance, in DICOMweb XML format"); @@ -417,7 +422,22 @@ } } - context.AnswerAttachment(call.GetOutput(), publicId, FileContentType_Dicom); + if (call.HasArgument(TRANSCODE)) + { + std::string source; + std::string transcoded; + context.ReadDicom(source, publicId); + + if (context.TranscodeWithCache(transcoded, source, publicId, GetTransferSyntax(call.GetArgument(TRANSCODE, "")))) + { + call.GetOutput().AnswerBuffer(transcoded, MimeType_Dicom); + } + } + else + { + // return the attachment without any transcoding + context.AnswerAttachment(call.GetOutput(), publicId, FileContentType_Dicom); + } }
--- a/OrthancServer/Sources/ServerContext.cpp Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancServer/Sources/ServerContext.cpp Thu Nov 16 16:09:04 2023 +0100 @@ -1948,6 +1948,36 @@ } + bool ServerContext::TranscodeWithCache(std::string& target, + const std::string& source, + const std::string& sourceInstanceId, + DicomTransferSyntax targetSyntax) + { + StorageCache::Accessor cacheAccessor(storageCache_); + + if (!cacheAccessor.FetchTranscodedInstance(target, sourceInstanceId, targetSyntax)) + { + IDicomTranscoder::DicomImage sourceDicom; + sourceDicom.SetExternalBuffer(source); + + IDicomTranscoder::DicomImage targetDicom; + std::set<DicomTransferSyntax> syntaxes; + syntaxes.insert(targetSyntax); + + if (Transcode(targetDicom, sourceDicom, syntaxes, true)) + { + cacheAccessor.AddTranscodedInstance(sourceInstanceId, targetSyntax, reinterpret_cast<const char*>(targetDicom.GetBufferData()), targetDicom.GetBufferSize()); + target = std::string(reinterpret_cast<const char*>(targetDicom.GetBufferData()), targetDicom.GetBufferSize()); + return true; + } + + return false; + } + + return true; + } + + bool ServerContext::Transcode(DicomImage& target, DicomImage& source /* in, "GetParsed()" possibly modified */, const std::set<DicomTransferSyntax>& allowedSyntaxes,
--- a/OrthancServer/Sources/ServerContext.h Wed Nov 15 08:58:45 2023 +0100 +++ b/OrthancServer/Sources/ServerContext.h Thu Nov 16 16:09:04 2023 +0100 @@ -556,6 +556,11 @@ const std::set<DicomTransferSyntax>& allowedSyntaxes, bool allowNewSopInstanceUid) ORTHANC_OVERRIDE; + virtual bool TranscodeWithCache(std::string& target, + const std::string& source, + const std::string& sourceInstanceId, + DicomTransferSyntax targetSyntax); + bool IsTranscodeDicomProtocol() const { return transcodeDicomProtocol_;