changeset 3931:e6606d3ec892 transcoding

new configuration option: BuiltinDecoderTranscoderOrder
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 15 May 2020 09:22:31 +0200
parents b99acc213937
children bf46e10de8ae
files NEWS OrthancServer/ServerContext.cpp OrthancServer/ServerContext.h OrthancServer/ServerEnumerations.cpp OrthancServer/ServerEnumerations.h Plugins/Engine/OrthancPlugins.cpp Plugins/Engine/OrthancPlugins.h Resources/Configuration.json
diffstat 8 files changed, 135 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- 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
 --------
--- 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<ImageAccessor> 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<ImageAccessor> 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<ImageAccessor> 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<DicomTransferSyntax>& allowedSyntaxes,
                                    bool allowNewSopInstanceUid)
   {
+    if (builtinDecoderTranscoderOrder_ == BuiltinDecoderTranscoderOrder_Before)
+    {
+      std::unique_ptr<IDicomTranscoder::TranscodedDicom> 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<IDicomTranscoder::TranscodedDicom> 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;
+    }
   }
 }
--- 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<IDicomTranscoder>  dcmtkTranscoder_;
+    BuiltinDecoderTranscoderOrder builtinDecoderTranscoderOrder_;
 
     StoreStatus StoreAfterTranscoding(std::string& resultPublicId,
                                       DicomInstanceToStore& dicom,
--- 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,
--- 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);
--- 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<boost::shared_mutex> lock(pimpl_->decoderTranscoderMutex_);
+    return !pimpl_->transcoderCallbacks_.empty();
+  }
+
+
   ImageAccessor* OrthancPlugins::Decode(const void* dicom,
                                         size_t size,
                                         unsigned int frame)
--- 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;
--- 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"
 }