# HG changeset patch # User Sebastien Jodogne # Date 1589527351 -7200 # Node ID e6606d3ec89268047f63f90cd828f4402fca7ea0 # Parent b99acc2139372b559baf0599ff0a1a8b018c531d new configuration option: BuiltinDecoderTranscoderOrder diff -r b99acc213937 -r e6606d3ec892 NEWS --- a/NEWS Thu May 14 19:20:40 2020 +0200 +++ b/NEWS Fri May 15 09:22:31 2020 +0200 @@ -7,6 +7,8 @@ * DICOM transcoding over the REST API * Transcoding from compressed to uncompressed transfer syntaxes over DICOM C-STORE SCU (if the remote modality doesn't support compressed syntaxes) +* New configuration options: "TranscodeDicomProtocol" and + "BuiltinDecoderTranscoderOrder" REST API -------- diff -r b99acc213937 -r e6606d3ec892 OrthancServer/ServerContext.cpp --- a/OrthancServer/ServerContext.cpp Thu May 14 19:20:40 2020 +0200 +++ b/OrthancServer/ServerContext.cpp Fri May 15 09:22:31 2020 +0200 @@ -268,8 +268,9 @@ // New configuration option in Orthanc 1.6.0 storageCommitmentReports_.reset(new StorageCommitmentReports(lock.GetConfiguration().GetUnsignedIntegerParameter("StorageCommitmentReportsSize", 100))); - // New option in Orthanc 1.7.0 + // New options in Orthanc 1.7.0 transcodeDicomProtocol_ = lock.GetConfiguration().GetBooleanParameter("TranscodeDicomProtocol", true); + builtinDecoderTranscoderOrder_ = StringToBuiltinDecoderTranscoderOrder(lock.GetConfiguration().GetStringParameter("BuiltinDecoderTranscoderOrder", "After")); } jobsEngine_.SetThreadSleep(unitTesting ? 20 : 200); @@ -1175,7 +1176,18 @@ ImageAccessor* ServerContext::DecodeDicomFrame(const std::string& publicId, unsigned int frameIndex) { - // TODO => Reorder given the global parameter + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before) + { + // Use Orthanc's built-in decoder, using the cache to speed-up + // things on multi-frame images + ServerContext::DicomCacheLocker locker(*this, publicId); + std::unique_ptr decoded( + DicomImageDecoder::Decode(locker.GetDicom(), frameIndex)); + if (decoded.get() != NULL) + { + return decoded.release(); + } + } #if ORTHANC_ENABLE_PLUGINS == 1 if (HasPlugins() && @@ -1184,14 +1196,13 @@ // TODO: Store the raw buffer in the DicomCacheLocker std::string dicomContent; ReadDicom(dicomContent, publicId); - std::unique_ptr decoded( GetPlugins().Decode(dicomContent.c_str(), dicomContent.size(), frameIndex)); if (decoded.get() != NULL) { return decoded.release(); } - else + else if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) { LOG(INFO) << "The installed image decoding plugins cannot handle an image, " << "fallback to the built-in DCMTK decoder"; @@ -1199,19 +1210,30 @@ } #endif + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) { - // Use Orthanc's built-in decoder, using the cache to speed-up - // things on multi-frame images ServerContext::DicomCacheLocker locker(*this, publicId); return DicomImageDecoder::Decode(locker.GetDicom(), frameIndex); } + else + { + return NULL; // Built-in decoder is disabled + } } ImageAccessor* ServerContext::DecodeDicomFrame(const DicomInstanceToStore& dicom, unsigned int frameIndex) { - // TODO => Reorder given the global parameter + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before) + { + std::unique_ptr decoded( + DicomImageDecoder::Decode(dicom.GetParsedDicomFile(), frameIndex)); + if (decoded.get() != NULL) + { + return decoded.release(); + } + } #if ORTHANC_ENABLE_PLUGINS == 1 if (HasPlugins() && @@ -1223,7 +1245,7 @@ { return decoded.release(); } - else + else if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) { LOG(INFO) << "The installed image decoding plugins cannot handle an image, " << "fallback to the built-in DCMTK decoder"; @@ -1231,7 +1253,14 @@ } #endif - return DicomImageDecoder::Decode(dicom.GetParsedDicomFile(), frameIndex); + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) + { + return DicomImageDecoder::Decode(dicom.GetParsedDicomFile(), frameIndex); + } + else + { + return NULL; + } } @@ -1265,24 +1294,41 @@ DicomTransferSyntax targetSyntax, bool allowNewSopInstanceUid) { + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before) + { + if (dcmtkTranscoder_->TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom, + targetSyntax, allowNewSopInstanceUid)) + { + return true; + } + } + #if ORTHANC_ENABLE_PLUGINS == 1 - if (HasPlugins()) + if (HasPlugins() && + GetPlugins().HasCustomTranscoder()) { if (GetPlugins().TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom, targetSyntax, allowNewSopInstanceUid)) { return true; } - else + else if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) { LOG(INFO) << "The installed transcoding plugins cannot handle an image, " << "fallback to the built-in DCMTK transcoder"; } } #endif - - return dcmtkTranscoder_->TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom, - targetSyntax, allowNewSopInstanceUid); + + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) + { + return dcmtkTranscoder_->TranscodeParsedToBuffer(target, hasSopInstanceUidChanged, dicom, + targetSyntax, allowNewSopInstanceUid); + } + else + { + return false; + } } @@ -1293,8 +1339,20 @@ const std::set& allowedSyntaxes, bool allowNewSopInstanceUid) { + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before) + { + std::unique_ptr transcoded( + dcmtkTranscoder_->TranscodeToParsed(dicom, buffer, size, allowedSyntaxes, + allowNewSopInstanceUid)); + if (transcoded.get() != NULL) + { + return transcoded.release(); + } + } + #if ORTHANC_ENABLE_PLUGINS == 1 - if (HasPlugins()) + if (HasPlugins() && + GetPlugins().HasCustomTranscoder()) { std::unique_ptr transcoded( GetPlugins().TranscodeToParsed(dicom, buffer, size, allowedSyntaxes, allowNewSopInstanceUid)); @@ -1303,7 +1361,7 @@ { return transcoded.release(); } - else + else if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) { LOG(INFO) << "The installed transcoding plugins cannot handle an image, " << "fallback to the built-in DCMTK transcoder"; @@ -1311,7 +1369,14 @@ } #endif - return dcmtkTranscoder_->TranscodeToParsed( - dicom, buffer, size, allowedSyntaxes, allowNewSopInstanceUid); + if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_After) + { + return dcmtkTranscoder_->TranscodeToParsed( + dicom, buffer, size, allowedSyntaxes, allowNewSopInstanceUid); + } + else + { + return NULL; + } } } diff -r b99acc213937 -r e6606d3ec892 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Thu May 14 19:20:40 2020 +0200 +++ b/OrthancServer/ServerContext.h Fri May 15 09:22:31 2020 +0200 @@ -229,6 +229,7 @@ bool transcodeDicomProtocol_; std::unique_ptr dcmtkTranscoder_; + BuiltinDecoderTranscoderOrder builtinDecoderTranscoderOrder_; StoreStatus StoreAfterTranscoding(std::string& resultPublicId, DicomInstanceToStore& dicom, diff -r b99acc213937 -r e6606d3ec892 OrthancServer/ServerEnumerations.cpp --- a/OrthancServer/ServerEnumerations.cpp Thu May 14 19:20:40 2020 +0200 +++ b/OrthancServer/ServerEnumerations.cpp Fri May 15 09:22:31 2020 +0200 @@ -214,6 +214,29 @@ "should be \"Always\", \"Never\" or \"Answers\": " + value); } } + + + BuiltinDecoderTranscoderOrder StringToBuiltinDecoderTranscoderOrder(const std::string& value) + { + if (value == "Before") + { + return BuiltinDecoderTranscoderOrder_Before; + } + else if (value == "After") + { + return BuiltinDecoderTranscoderOrder_After; + } + else if (value == "Disabled") + { + return BuiltinDecoderTranscoderOrder_Disabled; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange, + "Configuration option \"BuiltinDecoderTranscoderOrder\" " + "should be \"After\", \"Before\" or \"Disabled\": " + value); + } + } std::string GetBasePath(ResourceType type, diff -r b99acc213937 -r e6606d3ec892 OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Thu May 14 19:20:40 2020 +0200 +++ b/OrthancServer/ServerEnumerations.h Fri May 15 09:22:31 2020 +0200 @@ -175,6 +175,13 @@ ChangeType_NewChildInstance = 4097 }; + enum BuiltinDecoderTranscoderOrder + { + BuiltinDecoderTranscoderOrder_Before, + BuiltinDecoderTranscoderOrder_After, + BuiltinDecoderTranscoderOrder_Disabled + }; + void InitializeServerEnumerations(); @@ -194,6 +201,8 @@ FindStorageAccessMode StringToFindStorageAccessMode(const std::string& str); + BuiltinDecoderTranscoderOrder StringToBuiltinDecoderTranscoderOrder(const std::string& str); + std::string EnumerationToString(FileContentType type); std::string GetFileContentMime(FileContentType type); diff -r b99acc213937 -r e6606d3ec892 Plugins/Engine/OrthancPlugins.cpp --- a/Plugins/Engine/OrthancPlugins.cpp Thu May 14 19:20:40 2020 +0200 +++ b/Plugins/Engine/OrthancPlugins.cpp Fri May 15 09:22:31 2020 +0200 @@ -4861,6 +4861,13 @@ } + bool OrthancPlugins::HasCustomTranscoder() + { + boost::shared_lock lock(pimpl_->decoderTranscoderMutex_); + return !pimpl_->transcoderCallbacks_.empty(); + } + + ImageAccessor* OrthancPlugins::Decode(const void* dicom, size_t size, unsigned int frame) diff -r b99acc213937 -r e6606d3ec892 Plugins/Engine/OrthancPlugins.h --- a/Plugins/Engine/OrthancPlugins.h Thu May 14 19:20:40 2020 +0200 +++ b/Plugins/Engine/OrthancPlugins.h Fri May 15 09:22:31 2020 +0200 @@ -328,6 +328,8 @@ bool HasCustomImageDecoder(); + bool HasCustomTranscoder(); + virtual ImageAccessor* Decode(const void* dicom, size_t size, unsigned int frame) ORTHANC_OVERRIDE; diff -r b99acc213937 -r e6606d3ec892 Resources/Configuration.json --- a/Resources/Configuration.json Thu May 14 19:20:40 2020 +0200 +++ b/Resources/Configuration.json Fri May 15 09:22:31 2020 +0200 @@ -546,5 +546,12 @@ // Whether Orthanc transcodes DICOM files to an uncompressed // transfer syntax over the DICOM protocol, if the remote modality // does not support compressed transfer syntaxes (new in Orthanc 1.7.0). - "TranscodeDicomProtocol" : true + "TranscodeDicomProtocol" : true, + + // If some plugin to decode/transcode DICOM instances is installed, + // this option specifies whether the built-in decoder/transcoder of + // Orthanc (that uses DCMTK) is applied before or after the plugins, + // or is not applied at all. The allowed values for this option are + // "After" (default value), "Before", or "Disabled". (new in Orthanc 1.7.0) + "BuiltinDecoderTranscoderOrder" : "After" }