Mercurial > hg > orthanc
comparison OrthancServer/ServerJobs/ArchiveJob.cpp @ 3913:6ddad3e0b569 transcoding
transcoding ZIP archive and media
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 08 May 2020 19:15:28 +0200 |
parents | 56f2397f027a |
children | b99acc213937 |
comparison
equal
deleted
inserted
replaced
3912:7610af1532c3 | 3913:6ddad3e0b569 |
---|---|
35 #include "ArchiveJob.h" | 35 #include "ArchiveJob.h" |
36 | 36 |
37 #include "../../Core/Cache/SharedArchive.h" | 37 #include "../../Core/Cache/SharedArchive.h" |
38 #include "../../Core/Compression/HierarchicalZipWriter.h" | 38 #include "../../Core/Compression/HierarchicalZipWriter.h" |
39 #include "../../Core/DicomParsing/DicomDirWriter.h" | 39 #include "../../Core/DicomParsing/DicomDirWriter.h" |
40 #include "../../Core/DicomParsing/FromDcmtkBridge.h" | |
40 #include "../../Core/Logging.h" | 41 #include "../../Core/Logging.h" |
41 #include "../../Core/OrthancException.h" | 42 #include "../../Core/OrthancException.h" |
42 #include "../OrthancConfiguration.h" | 43 #include "../OrthancConfiguration.h" |
43 #include "../ServerContext.h" | 44 #include "../ServerContext.h" |
44 | 45 |
53 | 54 |
54 static const char* const MEDIA_IMAGES_FOLDER = "IMAGES"; | 55 static const char* const MEDIA_IMAGES_FOLDER = "IMAGES"; |
55 static const char* const KEY_DESCRIPTION = "Description"; | 56 static const char* const KEY_DESCRIPTION = "Description"; |
56 static const char* const KEY_INSTANCES_COUNT = "InstancesCount"; | 57 static const char* const KEY_INSTANCES_COUNT = "InstancesCount"; |
57 static const char* const KEY_UNCOMPRESSED_SIZE_MB = "UncompressedSizeMB"; | 58 static const char* const KEY_UNCOMPRESSED_SIZE_MB = "UncompressedSizeMB"; |
59 static const char* const KEY_TRANSCODE = "Transcode"; | |
58 | 60 |
59 | 61 |
60 namespace Orthanc | 62 namespace Orthanc |
61 { | 63 { |
62 static bool IsZip64Required(uint64_t uncompressedSize, | 64 static bool IsZip64Required(uint64_t uncompressedSize, |
397 } | 399 } |
398 | 400 |
399 void Apply(HierarchicalZipWriter& writer, | 401 void Apply(HierarchicalZipWriter& writer, |
400 ServerContext& context, | 402 ServerContext& context, |
401 DicomDirWriter* dicomDir, | 403 DicomDirWriter* dicomDir, |
402 const std::string& dicomDirFolder) const | 404 const std::string& dicomDirFolder, |
405 bool transcode, | |
406 DicomTransferSyntax transferSyntax) const | |
403 { | 407 { |
404 switch (type_) | 408 switch (type_) |
405 { | 409 { |
406 case Type_OpenDirectory: | 410 case Type_OpenDirectory: |
407 writer.OpenDirectory(filename_.c_str()); | 411 writer.OpenDirectory(filename_.c_str()); |
424 LOG(WARNING) << "An instance was removed after the job was issued: " << instanceId_; | 428 LOG(WARNING) << "An instance was removed after the job was issued: " << instanceId_; |
425 return; | 429 return; |
426 } | 430 } |
427 | 431 |
428 //boost::this_thread::sleep(boost::posix_time::milliseconds(300)); | 432 //boost::this_thread::sleep(boost::posix_time::milliseconds(300)); |
433 | |
434 writer.OpenFile(filename_.c_str()); | |
435 | |
436 bool transcodeSuccess = false; | |
437 | |
438 std::unique_ptr<ParsedDicomFile> parsed; | |
429 | 439 |
430 writer.OpenFile(filename_.c_str()); | 440 if (transcode) |
431 writer.Write(content); | |
432 | |
433 if (dicomDir != NULL) | |
434 { | 441 { |
435 ParsedDicomFile parsed(content); | 442 // New in Orthanc 1.7.0 |
436 dicomDir->Add(dicomDirFolder, filename_, parsed); | 443 std::set<DicomTransferSyntax> syntaxes; |
444 syntaxes.insert(transferSyntax); | |
445 | |
446 parsed.reset(new ParsedDicomFile(content)); | |
447 const char* data = content.empty() ? NULL : content.c_str(); | |
448 | |
449 std::unique_ptr<IDicomTranscoder::TranscodedDicom> transcodedDicom( | |
450 context.GetTranscoder().TranscodeToParsed( | |
451 parsed->GetDcmtkObject(), data, content.size(), | |
452 syntaxes, true /* allow new SOP instance UID */)); | |
453 | |
454 if (transcodedDicom.get() != NULL && | |
455 transcodedDicom->GetDicom().getDataset() != NULL) | |
456 { | |
457 std::string transcoded; | |
458 FromDcmtkBridge::SaveToMemoryBuffer( | |
459 transcoded, *transcodedDicom->GetDicom().getDataset()); | |
460 | |
461 writer.Write(transcoded); | |
462 | |
463 if (dicomDir != NULL) | |
464 { | |
465 std::unique_ptr<ParsedDicomFile> tmp( | |
466 ParsedDicomFile::AcquireDcmtkObject(transcodedDicom->ReleaseDicom())); | |
467 dicomDir->Add(dicomDirFolder, filename_, *tmp); | |
468 } | |
469 | |
470 transcodeSuccess = true; | |
471 } | |
472 else | |
473 { | |
474 LOG(INFO) << "Cannot transcode instance " << instanceId_ | |
475 << " to transfer syntax: " << GetTransferSyntaxUid(transferSyntax); | |
476 } | |
477 } | |
478 | |
479 if (!transcodeSuccess) | |
480 { | |
481 writer.Write(content); | |
482 | |
483 if (dicomDir != NULL) | |
484 { | |
485 if (parsed.get() == NULL) | |
486 { | |
487 parsed.reset(new ParsedDicomFile(content)); | |
488 } | |
489 | |
490 dicomDir->Add(dicomDirFolder, filename_, *parsed); | |
491 } | |
437 } | 492 } |
438 | 493 |
439 break; | 494 break; |
440 } | 495 } |
441 | 496 |
452 | 507 |
453 void ApplyInternal(HierarchicalZipWriter& writer, | 508 void ApplyInternal(HierarchicalZipWriter& writer, |
454 ServerContext& context, | 509 ServerContext& context, |
455 size_t index, | 510 size_t index, |
456 DicomDirWriter* dicomDir, | 511 DicomDirWriter* dicomDir, |
457 const std::string& dicomDirFolder) const | 512 const std::string& dicomDirFolder, |
513 bool transcode, | |
514 DicomTransferSyntax transferSyntax) const | |
458 { | 515 { |
459 if (index >= commands_.size()) | 516 if (index >= commands_.size()) |
460 { | 517 { |
461 throw OrthancException(ErrorCode_ParameterOutOfRange); | 518 throw OrthancException(ErrorCode_ParameterOutOfRange); |
462 } | 519 } |
463 | 520 |
464 commands_[index]->Apply(writer, context, dicomDir, dicomDirFolder); | 521 commands_[index]->Apply(writer, context, dicomDir, dicomDirFolder, transcode, transferSyntax); |
465 } | 522 } |
466 | 523 |
467 public: | 524 public: |
468 ZipCommands() : | 525 ZipCommands() : |
469 uncompressedSize_(0), | 526 uncompressedSize_(0), |
494 uint64_t GetUncompressedSize() const | 551 uint64_t GetUncompressedSize() const |
495 { | 552 { |
496 return uncompressedSize_; | 553 return uncompressedSize_; |
497 } | 554 } |
498 | 555 |
556 // "media" flavor (with DICOMDIR) | |
499 void Apply(HierarchicalZipWriter& writer, | 557 void Apply(HierarchicalZipWriter& writer, |
500 ServerContext& context, | 558 ServerContext& context, |
501 size_t index, | 559 size_t index, |
502 DicomDirWriter& dicomDir, | 560 DicomDirWriter& dicomDir, |
503 const std::string& dicomDirFolder) const | 561 const std::string& dicomDirFolder, |
504 { | 562 bool transcode, |
505 ApplyInternal(writer, context, index, &dicomDir, dicomDirFolder); | 563 DicomTransferSyntax transferSyntax) const |
506 } | 564 { |
507 | 565 ApplyInternal(writer, context, index, &dicomDir, dicomDirFolder, transcode, transferSyntax); |
566 } | |
567 | |
568 // "archive" flavor (without DICOMDIR) | |
508 void Apply(HierarchicalZipWriter& writer, | 569 void Apply(HierarchicalZipWriter& writer, |
509 ServerContext& context, | 570 ServerContext& context, |
510 size_t index) const | 571 size_t index, |
511 { | 572 bool transcode, |
512 ApplyInternal(writer, context, index, NULL, ""); | 573 DicomTransferSyntax transferSyntax) const |
574 { | |
575 ApplyInternal(writer, context, index, NULL, "", transcode, transferSyntax); | |
513 } | 576 } |
514 | 577 |
515 void AddOpenDirectory(const std::string& filename) | 578 void AddOpenDirectory(const std::string& filename) |
516 { | 579 { |
517 commands_.push_back(new Command(Type_OpenDirectory, filename)); | 580 commands_.push_back(new Command(Type_OpenDirectory, filename)); |
738 size_t GetStepsCount() const | 801 size_t GetStepsCount() const |
739 { | 802 { |
740 return commands_.GetSize() + 1; | 803 return commands_.GetSize() + 1; |
741 } | 804 } |
742 | 805 |
743 void RunStep(size_t index) | 806 void RunStep(size_t index, |
807 bool transcode, | |
808 DicomTransferSyntax transferSyntax) | |
744 { | 809 { |
745 if (index > commands_.GetSize()) | 810 if (index > commands_.GetSize()) |
746 { | 811 { |
747 throw OrthancException(ErrorCode_ParameterOutOfRange); | 812 throw OrthancException(ErrorCode_ParameterOutOfRange); |
748 } | 813 } |
762 else | 827 else |
763 { | 828 { |
764 if (isMedia_) | 829 if (isMedia_) |
765 { | 830 { |
766 assert(dicomDir_.get() != NULL); | 831 assert(dicomDir_.get() != NULL); |
767 commands_.Apply(*zip_, context_, index, *dicomDir_, MEDIA_IMAGES_FOLDER); | 832 commands_.Apply(*zip_, context_, index, *dicomDir_, |
833 MEDIA_IMAGES_FOLDER, transcode, transferSyntax); | |
768 } | 834 } |
769 else | 835 else |
770 { | 836 { |
771 assert(dicomDir_.get() == NULL); | 837 assert(dicomDir_.get() == NULL); |
772 commands_.Apply(*zip_, context_, index); | 838 commands_.Apply(*zip_, context_, index, transcode, transferSyntax); |
773 } | 839 } |
774 } | 840 } |
775 } | 841 } |
776 | 842 |
777 unsigned int GetInstancesCount() const | 843 unsigned int GetInstancesCount() const |
793 archive_(new ArchiveIndex(ResourceType_Patient)), // root | 859 archive_(new ArchiveIndex(ResourceType_Patient)), // root |
794 isMedia_(isMedia), | 860 isMedia_(isMedia), |
795 enableExtendedSopClass_(enableExtendedSopClass), | 861 enableExtendedSopClass_(enableExtendedSopClass), |
796 currentStep_(0), | 862 currentStep_(0), |
797 instancesCount_(0), | 863 instancesCount_(0), |
798 uncompressedSize_(0) | 864 uncompressedSize_(0), |
865 transcode_(false), | |
866 transferSyntax_(DicomTransferSyntax_LittleEndianImplicit) | |
799 { | 867 { |
800 } | 868 } |
801 | 869 |
802 | 870 |
803 ArchiveJob::~ArchiveJob() | 871 ArchiveJob::~ArchiveJob() |
849 } | 917 } |
850 else | 918 else |
851 { | 919 { |
852 ResourceIdentifiers resource(context_.GetIndex(), publicId); | 920 ResourceIdentifiers resource(context_.GetIndex(), publicId); |
853 archive_->Add(context_.GetIndex(), resource); | 921 archive_->Add(context_.GetIndex(), resource); |
922 } | |
923 } | |
924 | |
925 | |
926 void ArchiveJob::SetTranscode(DicomTransferSyntax transferSyntax) | |
927 { | |
928 if (writer_.get() != NULL) // Already started | |
929 { | |
930 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
931 } | |
932 else | |
933 { | |
934 transcode_ = true; | |
935 transferSyntax_ = transferSyntax; | |
854 } | 936 } |
855 } | 937 } |
856 | 938 |
857 | 939 |
858 void ArchiveJob::Reset() | 940 void ArchiveJob::Reset() |
952 FinalizeTarget(); | 1034 FinalizeTarget(); |
953 return JobStepResult::Success(); | 1035 return JobStepResult::Success(); |
954 } | 1036 } |
955 else | 1037 else |
956 { | 1038 { |
957 writer_->RunStep(currentStep_); | 1039 writer_->RunStep(currentStep_, transcode_, transferSyntax_); |
958 | 1040 |
959 currentStep_ ++; | 1041 currentStep_ ++; |
960 | 1042 |
961 if (currentStep_ == writer_->GetStepsCount()) | 1043 if (currentStep_ == writer_->GetStepsCount()) |
962 { | 1044 { |
1004 value = Json::objectValue; | 1086 value = Json::objectValue; |
1005 value[KEY_DESCRIPTION] = description_; | 1087 value[KEY_DESCRIPTION] = description_; |
1006 value[KEY_INSTANCES_COUNT] = instancesCount_; | 1088 value[KEY_INSTANCES_COUNT] = instancesCount_; |
1007 value[KEY_UNCOMPRESSED_SIZE_MB] = | 1089 value[KEY_UNCOMPRESSED_SIZE_MB] = |
1008 static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES); | 1090 static_cast<unsigned int>(uncompressedSize_ / MEGA_BYTES); |
1091 | |
1092 if (transcode_) | |
1093 { | |
1094 value[KEY_TRANSCODE] = GetTransferSyntaxUid(transferSyntax_); | |
1095 } | |
1009 } | 1096 } |
1010 | 1097 |
1011 | 1098 |
1012 bool ArchiveJob::GetOutput(std::string& output, | 1099 bool ArchiveJob::GetOutput(std::string& output, |
1013 MimeType& mime, | 1100 MimeType& mime, |