# HG changeset patch # User Sebastien Jodogne # Date 1589992144 -7200 # Node ID 76a24be1291270b4839b7839fcf0484e3a17220f # Parent 596912ebab5fce27542b7b582432ebb4a511dbb8 c-get: support of transcoding diff -r 596912ebab5f -r 76a24be12912 Core/DicomNetworking/Internals/CommandDispatcher.cpp --- a/Core/DicomNetworking/Internals/CommandDispatcher.cpp Wed May 20 17:03:24 2020 +0200 +++ b/Core/DicomNetworking/Internals/CommandDispatcher.cpp Wed May 20 18:29:04 2020 +0200 @@ -523,7 +523,7 @@ #endif // now that C-GET SCP is always enabled, the first branch of this if is useless - // TO BE ANALIZED by SJ + // TO BE ANALYZED by SJ if (!server.HasGetRequestHandlerFactory()) // dcmqrsrv.cc line 828 { cond = ASC_acceptContextsWithPreferredTransferSyntaxes( diff -r 596912ebab5f -r 76a24be12912 Core/DicomNetworking/Internals/GetScp.cpp --- a/Core/DicomNetworking/Internals/GetScp.cpp Wed May 20 17:03:24 2020 +0200 +++ b/Core/DicomNetworking/Internals/GetScp.cpp Wed May 20 18:29:04 2020 +0200 @@ -163,8 +163,9 @@ try { - if (!data.handler_->Handle(input, data.remoteIp_, data.remoteAet_, data.calledAet_, - data.timeout_ < 0 ? 0 : static_cast(data.timeout_))) + if (!data.handler_->Handle( + input, data.remoteIp_, data.remoteAet_, data.calledAet_, + data.timeout_ < 0 ? 0 : static_cast(data.timeout_))) { response->DimseStatus = STATUS_GET_Failed_UnableToProcess; return; diff -r 596912ebab5f -r 76a24be12912 OrthancServer/OrthancGetRequestHandler.cpp --- a/OrthancServer/OrthancGetRequestHandler.cpp Wed May 20 17:03:24 2020 +0200 +++ b/OrthancServer/OrthancGetRequestHandler.cpp Wed May 20 18:29:04 2020 +0200 @@ -33,10 +33,11 @@ #include "PrecompiledHeadersServer.h" #include "OrthancGetRequestHandler.h" +#include +#include +#include #include #include -#include -#include #include #include "../../Core/DicomParsing/FromDcmtkBridge.h" @@ -48,7 +49,6 @@ #include "ServerJobs/DicomModalityStoreJob.h" - namespace Orthanc { namespace @@ -64,7 +64,8 @@ } } - OrthancGetRequestHandler::Status OrthancGetRequestHandler::DoNext(T_ASC_Association* assoc) + OrthancGetRequestHandler::Status + OrthancGetRequestHandler::DoNext(T_ASC_Association* assoc) { if (position_ >= instances_.size()) { @@ -81,14 +82,16 @@ return Status_Failure; } - ParsedDicomFile parsed(dicom); + std::unique_ptr parsed( + FromDcmtkBridge::LoadFromMemoryBuffer(dicom.c_str(), dicom.size())); - if (parsed.GetDcmtkObject().getDataset() == NULL) + if (parsed.get() == NULL || + parsed->getDataset() == NULL) { throw OrthancException(ErrorCode_InternalError); } - DcmDataset& dataset = *parsed.GetDcmtkObject().getDataset(); + DcmDataset& dataset = *parsed->getDataset(); OFString a, b; if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() || @@ -102,11 +105,11 @@ std::string sopClassUid(a.c_str()); std::string sopInstanceUid(b.c_str()); - OFCondition cond = PerformGetSubOp(assoc, sopClassUid, sopInstanceUid, dataset); + OFCondition cond = PerformGetSubOp(assoc, sopClassUid, sopInstanceUid, parsed.release()); if (getCancelled_) { - LOG(INFO) << "Get SCP: Received C-Cancel RQ"; + LOG(INFO) << "C-GET SCP: Received C-Cancel RQ"; } if (cond.bad() || getCancelled_) @@ -131,21 +134,131 @@ } + static bool SelectPresentationContext(T_ASC_PresentationContextID& selectedPresentationId, + DicomTransferSyntax& selectedSyntax, + T_ASC_Association* assoc, + const std::string& sopClassUid, + DicomTransferSyntax sourceSyntax, + bool allowTranscoding) + { + typedef std::map Accepted; + + Accepted accepted; + + /** + * 1. Inspect and index all the accepted transfer syntaxes. This + * is similar to the code from "DicomAssociation::Open()". + **/ + + LST_HEAD **l = &assoc->params->DULparams.acceptedPresentationContext; + if (*l != NULL) + { + DUL_PRESENTATIONCONTEXT* pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l); + LST_Position(l, (LST_NODE*)pc); + while (pc) + { + if (pc->result == ASC_P_ACCEPTANCE && + std::string(pc->abstractSyntax) == sopClassUid) + { + DicomTransferSyntax transferSyntax; + if (LookupTransferSyntax(transferSyntax, pc->acceptedTransferSyntax)) + { + accepted[transferSyntax] = pc->presentationContextID; + } + else + { + LOG(WARNING) << "C-GET: Unknown transfer syntax received: " + << pc->acceptedTransferSyntax; + } + } + + pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l); + } + } + + + /** + * 2. Select the preferred transfer syntaxes, which corresponds to + * the source transfer syntax, plus all the uncompressed transfer + * syntaxes if transcoding is enabled. + **/ + + std::list preferred; + preferred.push_back(sourceSyntax); + + if (allowTranscoding) + { + if (sourceSyntax != DicomTransferSyntax_LittleEndianImplicit) + { + // Default Transfer Syntax for DICOM + preferred.push_back(DicomTransferSyntax_LittleEndianImplicit); + } + + if (sourceSyntax != DicomTransferSyntax_LittleEndianExplicit) + { + preferred.push_back(DicomTransferSyntax_LittleEndianExplicit); + } + + if (sourceSyntax != DicomTransferSyntax_BigEndianExplicit) + { + // Retired + preferred.push_back(DicomTransferSyntax_BigEndianExplicit); + } + } + + + /** + * 3. Lookup whether one of the preferred transfer syntaxes was + * accepted. + **/ + + for (std::list::const_iterator + it = preferred.begin(); it != preferred.end(); ++it) + { + Accepted::const_iterator found = accepted.find(*it); + if (found != accepted.end()) + { + selectedPresentationId = found->second; + selectedSyntax = *it; + return true; + } + } + + // No preferred syntax was accepted + return false; + } + + OFCondition OrthancGetRequestHandler::PerformGetSubOp(T_ASC_Association* assoc, const std::string& sopClassUid, const std::string& sopInstanceUid, - DcmDataset& dataset) + DcmFileFormat* dicomRaw) { - T_ASC_PresentationContextID presId; + assert(dicomRaw != NULL); + std::unique_ptr dicom(dicomRaw); - // which presentation context should be used - presId = ASC_findAcceptedPresentationContextID(assoc, sopClassUid.c_str()); - - if (presId == 0) + DicomTransferSyntax sourceSyntax; + if (!FromDcmtkBridge::LookupOrthancTransferSyntax(sourceSyntax, *dicom)) { nFailed_++; AddFailedUIDInstance(sopInstanceUid); - LOG(ERROR) << "Get SCP: storeSCU: No presentation context for: (" + LOG(ERROR) << "C-GET SCP: Unknown transfer syntax: (" + << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; + return DIMSE_NOVALIDPRESENTATIONCONTEXTID; + } + + bool allowTranscoding = (context_.IsTranscodeDicomProtocol() && + remote_.IsTranscodingAllowed()); + + T_ASC_PresentationContextID presId; + DicomTransferSyntax selectedSyntax; + if (!SelectPresentationContext(presId, selectedSyntax, assoc, sopClassUid, + sourceSyntax, allowTranscoding) || + presId == 0) + { + nFailed_++; + AddFailedUIDInstance(sopInstanceUid); + LOG(ERROR) << "C-GET SCP: storeSCU: No presentation context for: (" << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; return DIMSE_NOVALIDPRESENTATIONCONTEXTID; } @@ -161,7 +274,7 @@ // the role is not appropriate nFailed_++; AddFailedUIDInstance(sopInstanceUid); - LOG(ERROR) <<"Get SCP: storeSCU: [No presentation context with requestor SCP role for: (" + LOG(ERROR) << "C-GET SCP: storeSCU: [No presentation context with requestor SCP role for: (" << dcmSOPClassUIDToModality(sopClassUid.c_str(), "OT") << ") " << sopClassUid; return DIMSE_NOVALIDPRESENTATIONCONTEXTID; } @@ -191,14 +304,49 @@ OFCondition cond; + if (sourceSyntax == selectedSyntax) { + // No transcoding is required DcmDataset *stDetailTmp = NULL; - cond = DIMSE_storeUser(assoc, presId, &req, NULL /* imageFileName */, &dataset, - GetSubOpProgressCallback, this /* callbackData */, - (timeout_ > 0 ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), timeout_, - &rsp, &stDetailTmp, &cancelParameters); + cond = DIMSE_storeUser( + assoc, presId, &req, NULL /* imageFileName */, dicom->getDataset(), + GetSubOpProgressCallback, this /* callbackData */, + (timeout_ > 0 ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), timeout_, + &rsp, &stDetailTmp, &cancelParameters); stDetail.reset(stDetailTmp); } + else + { + // Transcoding to the selected uncompressed transfer syntax + IDicomTranscoder::DicomImage source, transcoded; + source.AcquireParsed(dicom.release()); + + std::set ts; + ts.insert(selectedSyntax); + + if (context_.Transcode(transcoded, source, ts, true)) + { + // Transcoding has succeeded + DcmDataset *stDetailTmp = NULL; + cond = DIMSE_storeUser( + assoc, presId, &req, NULL /* imageFileName */, + transcoded.GetParsed().getDataset(), + GetSubOpProgressCallback, this /* callbackData */, + (timeout_ > 0 ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), timeout_, + &rsp, &stDetailTmp, &cancelParameters); + stDetail.reset(stDetailTmp); + } + else + { + // Cannot transcode + nFailed_++; + AddFailedUIDInstance(sopInstanceUid); + LOG(ERROR) << "C-GET SCP: Cannot transcode " << sopClassUid + << " from transfer syntax " << GetTransferSyntaxUid(sourceSyntax) + << " to " << GetTransferSyntaxUid(selectedSyntax); + return DIMSE_NOVALIDPRESENTATIONCONTEXTID; + } + } if (cond.good()) { @@ -211,7 +359,7 @@ } else { - LOG(ERROR) << "Get SCP: Unexpected C-Cancel-RQ encountered: pid=" << (int)cancelParameters.presId + LOG(ERROR) << "C-GET SCP: Unexpected C-Cancel-RQ encountered: pid=" << (int)cancelParameters.presId << ", mid=" << (int)cancelParameters.req.MessageIDBeingRespondedTo; } } @@ -225,7 +373,7 @@ { // a warning status message warningCount_++; - LOG(ERROR) << "Get SCP: Store Warning: Response Status: " + LOG(ERROR) << "C-GET SCP: Store Warning: Response Status: " << DU_cstoreStatusString(rsp.DimseStatus); } else @@ -233,7 +381,7 @@ nFailed_++; AddFailedUIDInstance(sopInstanceUid); // print a status message - LOG(ERROR) << "Get SCP: Store Failed: Response Status: " + LOG(ERROR) << "C-GET SCP: Store Failed: Response Status: " << DU_cstoreStatusString(rsp.DimseStatus); } } @@ -242,7 +390,7 @@ nFailed_++; AddFailedUIDInstance(sopInstanceUid); OFString temp_str; - LOG(ERROR) << "Get SCP: storeSCU: Store Request Failed: " << DimseCondition::dump(temp_str, cond); + LOG(ERROR) << "C-GET SCP: storeSCU: Store Request Failed: " << DimseCondition::dump(temp_str, cond); } if (stDetail.get() != NULL) @@ -343,7 +491,7 @@ { MetricsRegistry::Timer timer(context_.GetMetricsRegistry(), "orthanc_get_scp_duration_ms"); - LOG(WARNING) << "Get-SCU request received from AET \"" << originatorAet << "\""; + LOG(WARNING) << "C-GET-SCU request received from AET \"" << originatorAet << "\""; { DicomArray query(input); diff -r 596912ebab5f -r 76a24be12912 OrthancServer/OrthancGetRequestHandler.h --- a/OrthancServer/OrthancGetRequestHandler.h Wed May 20 17:03:24 2020 +0200 +++ b/OrthancServer/OrthancGetRequestHandler.h Wed May 20 18:29:04 2020 +0200 @@ -39,6 +39,8 @@ #include +class DcmFileFormat; + namespace Orthanc { class ServerContext; @@ -72,7 +74,7 @@ OFCondition PerformGetSubOp(T_ASC_Association *assoc, const std::string& sopClassUid, const std::string& sopInstanceUid, - DcmDataset& dataset); + DcmFileFormat* datasetRaw); void AddFailedUIDInstance(const std::string& sopInstance); diff -r 596912ebab5f -r 76a24be12912 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Wed May 20 17:03:24 2020 +0200 +++ b/OrthancServer/ServerContext.h Wed May 20 18:29:04 2020 +0200 @@ -483,5 +483,10 @@ DicomImage& source /* in, "GetParsed()" possibly modified */, const std::set& allowedSyntaxes, bool allowNewSopInstanceUid) ORTHANC_OVERRIDE; + + bool IsTranscodeDicomProtocol() const + { + return transcodeDicomProtocol_; + } }; }