Sync libimage_io to CL: #231067980

Changes included:
- [ddepth] Add GetItemPayload for istream
- Adds IStreamRefDataSource and OStreamRefDataDestination to the ImageIO
library.
- Adds GDepthMetadata and readers and writers.
- Adds XmpGImageMetadata and related classes/changes.
- Adds a motion photo builder program and enhances/fixes other
components.
- Adds some new util classes for use with motion photos builder/checker.
- Eliminates the global vars used by image_io::MessageHandler::Get().
- Adds ability to read and check the MP4 part of a motion photo.
- [rosie] Unify all minimum_os_version flags to a minimum_os.bzl file.
- Adds the beginnings of the motion photo checker feature.
- Adds camera and container metadata to motion_photos and updates video
metadata.
- Adds classes/functions to xml/xmp in preparation for motion photo XMP
processing.
- Adds classes/functions to base/xmp in preparation for motion photo XMP
processing.
- Adds the photos/editing/formats/motion_photo and image_io/iso
libraries.

Bug: 123316622
Test: Camera CTS
Change-Id: Ida94bd36cbe45346e493959c32dd713e1b786abb
diff --git a/includes/image_io/base/istream_data_source.h b/includes/image_io/base/istream_data_source.h
index 4564e14..9b87542 100644
--- a/includes/image_io/base/istream_data_source.h
+++ b/includes/image_io/base/istream_data_source.h
@@ -1,46 +1,28 @@
 #ifndef IMAGE_IO_BASE_ISTREAM_DATA_SOURCE_H_  // NOLINT
 #define IMAGE_IO_BASE_ISTREAM_DATA_SOURCE_H_  // NOLINT
 
-#include <iostream>
 #include <memory>
 #include <utility>
 
-#include "image_io/base/data_source.h"
+#include "image_io/base/istream_ref_data_source.h"
 
 namespace photos_editing_formats {
 namespace image_io {
 
-/// A DataSource that obtains data from an istream.
-class IStreamDataSource : public DataSource {
+/// A DataSource that obtains data from an istream that it owns.
+class IStreamDataSource : public IStreamRefDataSource {
  public:
   /// Constructs an IStreamDataSource using the given istream.
   /// @param istram_ptr The istream from which to read.
   explicit IStreamDataSource(std::unique_ptr<std::istream> istream_ptr)
-      : istream_(std::move(istream_ptr)) {}
-
-  void Reset() override;
-  std::shared_ptr<DataSegment> GetDataSegment(size_t begin,
-                                              size_t min_size) override;
-  TransferDataResult TransferData(const DataRange& data_range, size_t best_size,
-                                  DataDestination* data_destination) override;
+      : IStreamRefDataSource(*istream_ptr), istream_(std::move(istream_ptr)) {}
 
  private:
-  /// The worker function to create a DataSegment and fill it with the given
-  /// number of bytes read from the istream, starting at the given location.
-  /// @param begin The location in the istream at which to start reading.
-  /// @param count The number of bytes to read.
-  /// @return A DataSegment pointer, or nullptr if the read failed.
-  std::shared_ptr<DataSegment> Read(size_t begin, size_t count);
-
- private:
-  /// The istream from which to read.
+  /// The istream that is owned by this data source.
   std::unique_ptr<std::istream> istream_;
-
-  /// The current data segment that was read in the GetDataSegment() function.
-  std::shared_ptr<DataSegment> current_data_segment_;
 };
 
 }  // namespace image_io
 }  // namespace photos_editing_formats
 
-#endif // IMAGE_IO_BASE_ISTREAM_DATA_SOURCE_H_  // NOLINT
+#endif  // IMAGE_IO_BASE_ISTREAM_DATA_SOURCE_H_  // NOLINT
diff --git a/includes/image_io/base/istream_ref_data_source.h b/includes/image_io/base/istream_ref_data_source.h
new file mode 100644
index 0000000..2460bb1
--- /dev/null
+++ b/includes/image_io/base/istream_ref_data_source.h
@@ -0,0 +1,46 @@
+#ifndef IMAGE_IO_BASE_ISTREAM_REF_DATA_SOURCE_H_  // NOLINT
+#define IMAGE_IO_BASE_ISTREAM_REF_DATA_SOURCE_H_  // NOLINT
+
+#include <iostream>
+
+#include "image_io/base/data_source.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A DataSource that obtains data from an istream held as a reference.
+class IStreamRefDataSource : public DataSource {
+ public:
+  /// Constructs an IStreamDataSource using the given istream.
+  /// @param istream_ref The istream from which to read.
+  explicit IStreamRefDataSource(std::istream& istream_ref)
+      : istream_ref_(istream_ref) {}
+  IStreamRefDataSource(const IStreamRefDataSource&) = delete;
+  IStreamRefDataSource& operator=(const IStreamRefDataSource&) = delete;
+
+  void Reset() override;
+  std::shared_ptr<DataSegment> GetDataSegment(size_t begin,
+                                              size_t min_size) override;
+  TransferDataResult TransferData(const DataRange& data_range, size_t best_size,
+                                  DataDestination* data_destination) override;
+
+ private:
+  /// The worker function to create a DataSegment and fill it with the given
+  /// number of bytes read from the istream, starting at the given location.
+  /// @param begin The location in the istream at which to start reading.
+  /// @param count The number of bytes to read.
+  /// @return A DataSegment pointer, or nullptr if the read failed.
+  std::shared_ptr<DataSegment> Read(size_t begin, size_t count);
+
+ private:
+  /// The istream from which to read.
+  std::istream& istream_ref_;
+
+  /// The current data segment that was read in the GetDataSegment() function.
+  std::shared_ptr<DataSegment> current_data_segment_;
+};
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_BASE_ISTREAM_REF_DATA_SOURCE_H_  // NOLINT
diff --git a/includes/image_io/base/message.h b/includes/image_io/base/message.h
index 8c225d8..fef2fd6 100644
--- a/includes/image_io/base/message.h
+++ b/includes/image_io/base/message.h
@@ -15,6 +15,9 @@
     /// A Status message.
     kStatus,
 
+    /// A Warning message.
+    kWarning,
+
     /// An error from the stdlib was detected. The std::errno variable can be
     /// used to programmatically decide what to do, or use the std::strerror
     /// function to get a string description of the error.
@@ -66,6 +69,17 @@
   /// @return The text of the message.
   const std::string& GetText() const { return text_; }
 
+  /// @return Whether the message is an error message.
+  bool IsError() const {
+    return type_ != Message::kStatus && type_ != Message::kWarning;
+  }
+
+  /// @return Whether the message is a warning message.
+  bool IsWarning() const { return type_ == Message::kWarning; }
+
+  /// @return Whether the message is a status message.
+  bool IsStatus() const { return type_ == Message::kStatus; }
+
  private:
   /// The type of message.
   Type type_;
diff --git a/includes/image_io/base/message_handler.h b/includes/image_io/base/message_handler.h
index dc33679..b60a593 100644
--- a/includes/image_io/base/message_handler.h
+++ b/includes/image_io/base/message_handler.h
@@ -5,6 +5,7 @@
 #include <vector>
 
 #include "image_io/base/message.h"
+#include "image_io/base/message_stats.h"
 #include "image_io/base/message_store.h"
 #include "image_io/base/message_writer.h"
 
@@ -15,20 +16,9 @@
 /// to report status and error conditions.
 class MessageHandler {
  public:
-  /// Initializes the MessageHandler for client use. Multithread applications
-  /// might find this function useful to call in their initialization section,
-  /// to guarentee that threads will not create race conditions when calling the
-  /// Get function for the first time.
-  static void Init(std::unique_ptr<MessageWriter> message_writer,
-                   std::unique_ptr<MessageStore> message_store);
-
-  /// This function is thread-safe as long as the Init() function is called in
-  /// non-multiple-threaded startup code; if the Init() fucnction was not called
-  /// there may be race conditions that causes the message handler returned from
-  /// Get() called in one thread to be different from that returned by the call
-  /// in a different thread.
-  /// @return The message handler used by the code in this library.
-  static MessageHandler* Get();
+  /// The default constructor for MessageHandler creates a MessageWriter and
+  /// VectorMessageStore for handling writing and storing messages.
+  MessageHandler();
 
   /// Sets the message writer to use when ReportMessage() is called. If client
   /// code does not call this function, the MessageHandler returned by the Get()
@@ -55,14 +45,29 @@
   /// should call this function again so that memory is not leaked when it is
   /// done using this library.
   void ClearMessages() {
+    message_stats_->Clear();
     if (message_store_) {
       message_store_->ClearMessages();
     }
   }
 
   /// @return Whether the message handler's store has error messages or not.
-  bool HasErrorMessages() const {
-    return message_store_ ? message_store_->HasErrorMessages() : false;
+  bool HasErrorMessages() const { return GetErrorMessageCount() > 0; }
+
+  /// @return The number of error messages reported.
+  size_t GetErrorMessageCount() const { return message_stats_->error_count; }
+
+  /// @return The number of warning messages reported.
+  size_t GetWarningMessageCount() const {
+    return message_stats_->warning_count;
+  }
+
+  /// @return The number of status messages reported.
+  size_t GetStatusMessageCount() const { return message_stats_->status_count; }
+
+  /// @return The message stats object as a shared pointer.
+  std::shared_ptr<MessageStats> GetMessageStats() const {
+    return message_stats_;
   }
 
   /// @return The vector of errors maintained by the message handler's store.
@@ -85,15 +90,14 @@
   void ReportMessage(const Message& message);
 
  private:
-  MessageHandler() = default;
-  ~MessageHandler();
-
- private:
   /// The message writer used by ReportMessage, or null.
   std::unique_ptr<MessageWriter> message_writer_;
 
   /// The message store for saving messages for later, or null.
   std::unique_ptr<MessageStore> message_store_;
+
+  /// The message stats for counting messages.
+  std::shared_ptr<MessageStats> message_stats_;
 };
 
 }  // namespace image_io
diff --git a/includes/image_io/base/message_stats.h b/includes/image_io/base/message_stats.h
new file mode 100644
index 0000000..6b338f0
--- /dev/null
+++ b/includes/image_io/base/message_stats.h
@@ -0,0 +1,21 @@
+#ifndef IMAGE_IO_BASE_MESSAGE_STATS_H_  // NOLINT
+#define IMAGE_IO_BASE_MESSAGE_STATS_H_  // NOLINT
+
+#include "image_io/base/types.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A structure for holding message stats.
+struct MessageStats {
+  MessageStats() { Clear(); }
+  void Clear() { error_count = warning_count = status_count = 0; }
+  size_t error_count;
+  size_t warning_count;
+  size_t status_count;
+};
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_BASE_MESSAGE_STATS_H_  // NOLINT
diff --git a/includes/image_io/base/message_store.h b/includes/image_io/base/message_store.h
index 1d492f1..8db0b9f 100644
--- a/includes/image_io/base/message_store.h
+++ b/includes/image_io/base/message_store.h
@@ -61,7 +61,7 @@
   ErrorFlagMessageStore() : has_error_(false) {}
   void ClearMessages() override { has_error_ = false; }
   void AddMessage(const Message& message) override {
-    if (message.GetType() != Message::kStatus) {
+    if (message.IsError()) {
       has_error_ = true;
     }
   }
diff --git a/includes/image_io/base/ostream_data_destination.h b/includes/image_io/base/ostream_data_destination.h
index 15a1155..d0cf04c 100644
--- a/includes/image_io/base/ostream_data_destination.h
+++ b/includes/image_io/base/ostream_data_destination.h
@@ -1,58 +1,31 @@
 #ifndef IMAGE_IO_BASE_OSTREAM_DATA_DESTINATION_H_  // NOLINT
 #define IMAGE_IO_BASE_OSTREAM_DATA_DESTINATION_H_  // NOLINT
 
-#include <iostream>
 #include <memory>
-#include <string>
 #include <utility>
 
-#include "image_io/base/data_destination.h"
+#include "image_io/base/ostream_ref_data_destination.h"
 
 namespace photos_editing_formats {
 namespace image_io {
 
 /// A DataDestination that writes its output to an ostream.
-class OStreamDataDestination : public DataDestination {
+class OStreamDataDestination : public OStreamRefDataDestination {
  public:
   /// Constructs an OStreamDataDestination using the given ostream.
   /// @param ostream_ptr The ostream to which data is written.
-  explicit OStreamDataDestination(std::unique_ptr<std::ostream> ostream_ptr)
-      : ostream_(std::move(ostream_ptr)),
-        bytes_transferred_(0),
-        has_error_(false) {}
-
-  /// @param name A name to associate with the ostream. Used for error messages.
-  void SetName(const std::string& name) { name_ = name; }
-
-  /// @return The name associated with the ostream.
-  const std::string& GetName() const { return name_; }
-
-  /// @return The number of bytes written to the ostream.
-  size_t GetBytesTransferred() const override { return bytes_transferred_; }
-
-  /// @return True if errors were encountered while writing to the ostream.
-  bool HasError() const { return has_error_; }
-
-  void StartTransfer() override;
-  TransferStatus Transfer(const DataRange& transfer_range,
-                          const DataSegment& data_segment) override;
-  void FinishTransfer() override;
+  /// @param message_handler An option message handler for writing messages.
+  OStreamDataDestination(std::unique_ptr<std::ostream> ostream_ptr,
+                         MessageHandler* message_handler)
+      : OStreamRefDataDestination(*ostream_ptr, message_handler),
+        ostream_(std::move(ostream_ptr)) {}
 
  private:
-  /// The ostream written to.
+  /// The ostream that is owned by this data destination.
   std::unique_ptr<std::ostream> ostream_;
-
-  /// The number of bytes written so far.
-  size_t bytes_transferred_;
-
-  /// A (file) name to associate with the ostream, used with error messages.
-  std::string name_;
-
-  /// If true indicates an error has occurred writing to the ostream.
-  bool has_error_;
 };
 
 }  // namespace image_io
 }  // namespace photos_editing_formats
 
-#endif // IMAGE_IO_BASE_OSTREAM_DATA_DESTINATION_H_  // NOLINT
+#endif  // IMAGE_IO_BASE_OSTREAM_DATA_DESTINATION_H_  // NOLINT
diff --git a/includes/image_io/base/ostream_ref_data_destination.h b/includes/image_io/base/ostream_ref_data_destination.h
new file mode 100644
index 0000000..266e2be
--- /dev/null
+++ b/includes/image_io/base/ostream_ref_data_destination.h
@@ -0,0 +1,66 @@
+#ifndef IMAGE_IO_BASE_OSTREAM_REF_DATA_DESTINATION_H_  // NOLINT
+#define IMAGE_IO_BASE_OSTREAM_REF_DATA_DESTINATION_H_  // NOLINT
+
+#include <iostream>
+#include <string>
+
+#include "image_io/base/data_destination.h"
+#include "image_io/base/message_handler.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A DataDestination that writes its output to an ostream held as a reference.
+class OStreamRefDataDestination : public DataDestination {
+ public:
+  /// Constructs an OStreamDataDestination using the given ostream.
+  /// @param ostream_ref The ostream to which data is written.
+  /// @param message_handler An option message handler for writing messages.
+  OStreamRefDataDestination(std::ostream& ostream_ref,
+                            MessageHandler* message_handler)
+      : ostream_ref_(ostream_ref),
+        message_handler_(message_handler),
+        bytes_transferred_(0),
+        has_error_(false) {}
+  OStreamRefDataDestination(const OStreamRefDataDestination&) = delete;
+  OStreamRefDataDestination& operator=(const OStreamRefDataDestination&) =
+      delete;
+
+  /// @param name A name to associate with the ostream. Used for error messages.
+  void SetName(const std::string& name) { name_ = name; }
+
+  /// @return The name associated with the ostream.
+  const std::string& GetName() const { return name_; }
+
+  /// @return The number of bytes written to the ostream.
+  size_t GetBytesTransferred() const override { return bytes_transferred_; }
+
+  /// @return True if errors were encountered while writing to the ostream.
+  bool HasError() const { return has_error_; }
+
+  void StartTransfer() override;
+  TransferStatus Transfer(const DataRange& transfer_range,
+                          const DataSegment& data_segment) override;
+  void FinishTransfer() override;
+
+ private:
+  /// The ostream written to.
+  std::ostream& ostream_ref_;
+
+  /// An optional message handler to write messages to.
+  MessageHandler* message_handler_;
+
+  /// The number of bytes written so far.
+  size_t bytes_transferred_;
+
+  /// A (file) name to associate with the ostream, used with error messages.
+  std::string name_;
+
+  /// If true indicates an error has occurred writing to the ostream.
+  bool has_error_;
+};
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_BASE_OSTREAM_REF_DATA_DESTINATION_H_  // NOLINT
diff --git a/includes/image_io/base/types.h b/includes/image_io/base/types.h
index aaa88b3..ed49350 100644
--- a/includes/image_io/base/types.h
+++ b/includes/image_io/base/types.h
@@ -7,8 +7,14 @@
 namespace photos_editing_formats {
 namespace image_io {
 
-/// Byte is the noumenon unit of data.
+/// The various integer and byte types used in this package.
 using Byte = std::uint8_t;
+using Int32 = std::int32_t;
+using Int64 = std::int64_t;
+using UInt8 = std::uint8_t;
+using UInt16 = std::uint16_t;
+using UInt32 = std::uint32_t;
+using UInt64 = std::uint64_t;
 
 /// A Byte value and a validity flag.
 struct ValidatedByte {
diff --git a/includes/image_io/base/validated_number.h b/includes/image_io/base/validated_number.h
new file mode 100644
index 0000000..ead5ab3
--- /dev/null
+++ b/includes/image_io/base/validated_number.h
@@ -0,0 +1,38 @@
+#ifndef IMAGE_IO_BASE_VALIDATED_NUMBER_H_  // NOLINT
+#define IMAGE_IO_BASE_VALIDATED_NUMBER_H_  // NOLINT
+
+#include <sstream>
+#include <string>
+
+namespace photos_editing_formats {
+namespace image_io {
+
+template <class T>
+struct ValidatedNumber {
+  ValidatedNumber() : ValidatedNumber(T(), false) {}
+  ValidatedNumber(const T& value_, bool is_valid_)
+      : value(value_), is_valid(is_valid_) {}
+  using value_type = T;
+  T value;
+  bool is_valid;
+};
+
+template <class T>
+ValidatedNumber<T> GetValidatedNumber(const std::string& str) {
+  std::stringstream ss(str);
+  ValidatedNumber<T> result;
+  ss >> result.value;
+  if (!ss.fail()) {
+    std::string extra;
+    ss >> extra;
+    if (extra.empty()) {
+      result.is_valid = true;
+    }
+  }
+  return result;
+}
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_BASE_VALIDATED_NUMBER_H_  // NOLINT
diff --git a/includes/image_io/extras/base64_decoder_data_destination.h b/includes/image_io/extras/base64_decoder_data_destination.h
index cde1dbc..107ea4b 100644
--- a/includes/image_io/extras/base64_decoder_data_destination.h
+++ b/includes/image_io/extras/base64_decoder_data_destination.h
@@ -5,6 +5,7 @@
 #include <vector>
 
 #include "image_io/base/data_destination.h"
+#include "image_io/base/message_handler.h"
 
 namespace photos_editing_formats {
 namespace image_io {
@@ -16,8 +17,11 @@
  public:
   /// @param next_destination The next DataDestination in the chain which will
   /// be sent the decoded bytes received by the Transfer() function.
-  explicit Base64DecoderDataDestination(DataDestination* next_destination)
+  /// @param message_handler An optional message handler to write messages to.
+  Base64DecoderDataDestination(DataDestination* next_destination,
+                               MessageHandler* message_handler)
       : next_destination_(next_destination),
+        message_handler_(message_handler),
         next_decoded_location_(0),
         has_error_(false) {}
 
@@ -39,6 +43,9 @@
   /// The destination that the decoded data is sent to.
   DataDestination* next_destination_;
 
+  /// An optional message handler to write messages to.
+  MessageHandler* message_handler_;
+
   /// If the transfer_range parameter of the Transfer function does not have a
   /// length that is a multiple of 4, then the leftover bytes are placed in this
   /// vector and are prepended to the data in the next call to Transfer.
diff --git a/includes/image_io/gcontainer/gcontainer.h b/includes/image_io/gcontainer/gcontainer.h
index c0bd66f..118956c 100644
--- a/includes/image_io/gcontainer/gcontainer.h
+++ b/includes/image_io/gcontainer/gcontainer.h
@@ -1,6 +1,7 @@
 #ifndef IMAGE_IO_GCONTAINER_GCONTAINER_H_  // NOLINT
 #define IMAGE_IO_GCONTAINER_GCONTAINER_H_  // NOLINT
 
+#include <iostream>
 #include <string>
 #include <vector>
 
@@ -29,6 +30,11 @@
                          size_t file_start_offset, size_t file_length,
                          std::string* out_file_contents);
 
+// Used by AOSP.
+bool ParseFileAfterImageFromStream(size_t start_offset, size_t length,
+                                   std::istream& input_jpeg_stream,
+                                   std::string* out_contents);
+
 }  // namespace gcontainer
 }  // namespace image_io
 }  // namespace photos_editing_formats
diff --git a/includes/image_io/jpeg/jpeg_apple_depth_builder.h b/includes/image_io/jpeg/jpeg_apple_depth_builder.h
index 7f5c595..4c0f192 100644
--- a/includes/image_io/jpeg/jpeg_apple_depth_builder.h
+++ b/includes/image_io/jpeg/jpeg_apple_depth_builder.h
@@ -6,6 +6,7 @@
 #include "image_io/base/data_destination.h"
 #include "image_io/base/data_range.h"
 #include "image_io/base/data_source.h"
+#include "image_io/base/message_handler.h"
 
 namespace photos_editing_formats {
 namespace image_io {
@@ -14,8 +15,9 @@
 /// and original depth image.
 class JpegAppleDepthBuilder {
  public:
-  JpegAppleDepthBuilder()
-      : primary_image_data_source_(nullptr),
+  explicit JpegAppleDepthBuilder(MessageHandler* message_handler)
+      : message_handler_(message_handler),
+        primary_image_data_source_(nullptr),
         depth_image_data_source_(nullptr),
         data_destination_(nullptr) {}
 
@@ -69,6 +71,9 @@
   /// @param data_range The data range in the data source to transfer.
   bool TransferData(DataSource *data_source, const DataRange& data_range);
 
+  /// An optional message handler to write messages to.
+  MessageHandler* message_handler_;
+
   /// The data source containing the primary image.
   DataSource* primary_image_data_source_;
 
diff --git a/includes/image_io/jpeg/jpeg_image_extractor.h b/includes/image_io/jpeg/jpeg_image_extractor.h
index 91237e5..966f609 100644
--- a/includes/image_io/jpeg/jpeg_image_extractor.h
+++ b/includes/image_io/jpeg/jpeg_image_extractor.h
@@ -3,6 +3,7 @@
 
 #include "image_io/base/data_destination.h"
 #include "image_io/base/data_source.h"
+#include "image_io/base/message_handler.h"
 #include "image_io/jpeg/jpeg_info.h"
 
 namespace photos_editing_formats {
@@ -15,8 +16,12 @@
  public:
   /// @param jpeg_info The JpegInfo instance containing depth/image data.
   /// @param data_source The DataSource from which to transfer depth/image data.
-  JpegImageExtractor(const JpegInfo& jpeg_info, DataSource* data_source)
-      : jpeg_info_(jpeg_info), data_source_(data_source) {}
+  /// @param message_handler An optional message handler to write messages to.
+  JpegImageExtractor(const JpegInfo& jpeg_info, DataSource* data_source,
+                     MessageHandler* message_handler)
+      : jpeg_info_(jpeg_info),
+        data_source_(data_source),
+        message_handler_(message_handler) {}
 
   /// This function extracts the Apple depth image from the DataSource and sends
   /// the bytes to the DataDestination.
@@ -65,6 +70,9 @@
 
   /// The data source from which the images are extracted.
   DataSource* data_source_;
+
+  /// An optional message handler to write messages to.
+  MessageHandler* message_handler_;
 };
 
 }  // namespace image_io
diff --git a/includes/image_io/jpeg/jpeg_info_builder.h b/includes/image_io/jpeg/jpeg_info_builder.h
index ee4d611..a9f1928 100644
--- a/includes/image_io/jpeg/jpeg_info_builder.h
+++ b/includes/image_io/jpeg/jpeg_info_builder.h
@@ -34,6 +34,12 @@
   /// @param type The type of segment info to capture the value of.
   void SetCaptureSegmentBytes(const std::string& segment_info_type);
 
+  /// @return True if the segment is a primary Xmp segment.
+  bool IsPrimaryXmpSegment(const JpegSegment& segment) const;
+
+  /// @return True if the segment is an extended Xmp segment.
+  bool IsExtendedXmpSegment(const JpegSegment& segment) const;
+
   void Start(JpegScanner* scanner) override;
   void Process(JpegScanner* scanner, const JpegSegment& segment) override;
   void Finish(JpegScanner* scanner) override;
@@ -45,12 +51,6 @@
   /// @return True if the data members indicate Apple matte is present.
   bool HasAppleMatte() const;
 
-  /// @return True if the segment is a primary Xmp segment.
-  bool IsPrimaryXmpSegment(const JpegSegment& segment) const;
-
-  /// @return True if the segment is an extended Xmp segment.
-  bool IsExtendedXmpSegment(const JpegSegment& segment) const;
-
   /// @return True if the segment is an Mpf segment.
   bool IsMpfSegment(const JpegSegment& segment) const;
 
diff --git a/includes/image_io/jpeg/jpeg_scanner.h b/includes/image_io/jpeg/jpeg_scanner.h
index 0ab0488..932d3db 100644
--- a/includes/image_io/jpeg/jpeg_scanner.h
+++ b/includes/image_io/jpeg/jpeg_scanner.h
@@ -5,6 +5,7 @@
 
 #include "image_io/base/data_segment.h"
 #include "image_io/base/data_source.h"
+#include "image_io/base/message_handler.h"
 #include "image_io/jpeg/jpeg_marker.h"
 #include "image_io/jpeg/jpeg_segment_processor.h"
 
@@ -16,8 +17,9 @@
 /// examination.
 class JpegScanner {
  public:
-  JpegScanner()
-      : data_source_(nullptr),
+  explicit JpegScanner(MessageHandler* message_handler)
+      : message_handler_(message_handler),
+        data_source_(nullptr),
         segment_processor_(nullptr),
         current_location_(0),
         done_(false),
@@ -70,6 +72,9 @@
   void GetNextSegment();
 
  private:
+  /// An optional message handler to write messages to.
+  MessageHandler* message_handler_;
+
   /// The DataSource from which DataSegments are obtained.
   DataSource* data_source_;
 
diff --git a/includes/image_io/jpeg/jpeg_xmp_data_extractor.h b/includes/image_io/jpeg/jpeg_xmp_data_extractor.h
index 30d62a1..29b16b6 100644
--- a/includes/image_io/jpeg/jpeg_xmp_data_extractor.h
+++ b/includes/image_io/jpeg/jpeg_xmp_data_extractor.h
@@ -2,6 +2,7 @@
 #define IMAGE_IO_JPEG_JPEG_XMP_DATA_EXTRACTOR_H_  // NOLINT
 
 #include "image_io/base/data_destination.h"
+#include "image_io/base/message_handler.h"
 #include "image_io/jpeg/jpeg_info.h"
 
 namespace photos_editing_formats {
@@ -17,10 +18,13 @@
   /// @param data_destination The destination to which the extracted xmp data
   ///     is to be sent.
   JpegXmpDataExtractor(JpegXmpInfo::Type xmp_info_type, size_t segment_count,
-                       DataDestination* data_destination)
+                       DataDestination* data_destination,
+                       MessageHandler* message_handler)
       : xmp_info_type_(xmp_info_type),
         last_segment_index_(segment_count - 1),
         data_destination_(data_destination),
+        message_handler_(message_handler),
+        segment_index_(0),
         has_error_(false) {}
 
   /// Set the current segment index to the given value.
@@ -52,6 +56,9 @@
   /// The DataDestination that the extracted xmp data is sent to.
   DataDestination* data_destination_;
 
+  /// An optional message handler to write messages to.
+  MessageHandler* message_handler_;
+
   /// The xmp data is spread over one or more segments in the DataSource. This
   /// index tracks which one is being transferred.
   size_t segment_index_;
diff --git a/includes/image_io/utils/file_utils.h b/includes/image_io/utils/file_utils.h
index d1a469d..3a8d2c6 100644
--- a/includes/image_io/utils/file_utils.h
+++ b/includes/image_io/utils/file_utils.h
@@ -6,34 +6,35 @@
 #include <string>
 
 #include "image_io/base/data_segment.h"
+#include "image_io/base/message_handler.h"
 
 namespace photos_editing_formats {
 namespace image_io {
 
-/// A policy that controls whether an error is reported or not.
-enum class ReportErrorPolicy { kDontReportError, kReportError };
-
 /// @param file_name The name of the file to get the size in bytes of.
 /// @param size A pointer to a variable to receive the size.
 /// @return Whether file size was obtained properly.
 bool GetFileSize(const std::string& file_name, size_t* size);
 
 /// @param file_name The name of the file to open for output.
+/// @param message_handler Optional message handler to write messages to.
 /// @return An ostream pointer or nullptr if the open failed.
-std::unique_ptr<std::ostream> OpenOutputFile(
-    const std::string& file_name, ReportErrorPolicy report_error_policy);
+std::unique_ptr<std::ostream> OpenOutputFile(const std::string& file_name,
+                                             MessageHandler* message_handler);
 
 /// @param file_name The name of the file to open for input.
+/// @param message_handler Optional message handler to write messages to.
 /// @return An istream pointer or nullptr if the open failed.
-std::unique_ptr<std::istream> OpenInputFile(
-    const std::string& file_name, ReportErrorPolicy report_error_policy);
+std::unique_ptr<std::istream> OpenInputFile(const std::string& file_name,
+                                            MessageHandler* message_handler);
 
 /// Opens the named file for input, gets its size, and reads the entire contents
 /// into a data segment that is returned to the caller.
 /// @param file_name The name of the file to open for input.
+/// @param message_handler Optional message handler to write messages to.
 /// @return A DataSegment pointer or nullptr if the open and reading failed.
-std::shared_ptr<DataSegment> ReadEntireFile(
-    const std::string& file_name, ReportErrorPolicy report_error_policy);
+std::shared_ptr<DataSegment> ReadEntireFile(const std::string& file_name,
+                                            MessageHandler* message_handler);
 
 }  // namespace image_io
 }  // namespace photos_editing_formats
diff --git a/includes/image_io/utils/message_stats_writer.h b/includes/image_io/utils/message_stats_writer.h
new file mode 100644
index 0000000..24dd6b1
--- /dev/null
+++ b/includes/image_io/utils/message_stats_writer.h
@@ -0,0 +1,47 @@
+#ifndef IMAGE_IO_UTILS_MESSAGE_STATS_WRITER_H_  // NOLINT
+#define IMAGE_IO_UTILS_MESSAGE_STATS_WRITER_H_  // NOLINT
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "image_io/base/message_stats.h"
+#include "image_io/utils/string_outputter.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A class to write the message stats for error and warning counts. The output
+/// is written when the writer object is destroyed, making this a conveneient
+/// class to use in functions that have multiple return points and for which
+/// such output is desired at all return points.
+class MessageStatsWriter {
+ public:
+  /// @param message_stats The message stats object holding the counts.
+  /// @param outputter The outputter function to write the stats to.
+  /// @param name The name of the tool or function that is "finished".
+  MessageStatsWriter(const std::shared_ptr<MessageStats>& message_stats,
+                     const std::string& name, const StringOutputter& outputter)
+      : stats_(message_stats), outputter_(outputter), name_(name) {}
+
+  /// Writes the finished message with the stats to the outputter function.
+  ~MessageStatsWriter() {
+    const string kError = stats_->error_count == 1 ? "error" : "errors";
+    const string kWarning = stats_->warning_count == 1 ? "warning" : "warnings";
+    std::stringstream ss;
+    ss << std::endl
+       << name_ << " finished, " << stats_->error_count << " " << kError << ", "
+       << stats_->warning_count << " " << kWarning << std::endl;
+    outputter_(ss.str());
+  }
+
+ private:
+  std::shared_ptr<MessageStats> stats_;
+  StringOutputter outputter_;
+  std::string name_;
+};
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_UTILS_MESSAGE_STATS_WRITER_H_ // NOLINT
diff --git a/includes/image_io/utils/string_outputter.h b/includes/image_io/utils/string_outputter.h
new file mode 100644
index 0000000..b6ea013
--- /dev/null
+++ b/includes/image_io/utils/string_outputter.h
@@ -0,0 +1,19 @@
+#ifndef IMAGE_IO_UTILS_STRING_OUTPUTTER_H_  // NOLINT
+#define IMAGE_IO_UTILS_STRING_OUTPUTTER_H_  // NOLINT
+
+#include <functional>
+#include <string>
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A typedef for a function that accepts a string and writes it somewhere.
+/// These types of functions are typically used in command line tools to write
+/// the output of the tool to stdout or some other location. The function
+/// should not write its own new line at the end of the str.
+using StringOutputter = std::function<void(const std::string& str)>;
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_UTILS_STRING_OUTPUTTER_H_  // NOLINT
diff --git a/includes/image_io/utils/string_outputter_message_writer.h b/includes/image_io/utils/string_outputter_message_writer.h
new file mode 100644
index 0000000..f34d975
--- /dev/null
+++ b/includes/image_io/utils/string_outputter_message_writer.h
@@ -0,0 +1,28 @@
+#ifndef IMAGE_IO_UTILS_STRING_OUTPUTTER_MESSAGE_WRITER_H_  // NOLINT
+#define IMAGE_IO_UTILS_STRING_OUTPUTTER_MESSAGE_WRITER_H_  // NOLINT
+
+#include "image_io/base/message_writer.h"
+#include "image_io/utils/string_outputter.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+/// A MessageWriter that writes the messages to the StringOutputter function.
+class StringOutputterMessageWriter : public MessageWriter {
+ public:
+  /// @param outputter The outputter function to write messages to.
+  explicit StringOutputterMessageWriter(const StringOutputter& outputter)
+      : outputter_(outputter) {}
+  void WriteMessage(const Message& message) override {
+    outputter_(GetFormattedMessage(message));
+    outputter_("\n");
+  }
+
+ private:
+  StringOutputter outputter_;
+};
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
+
+#endif  // IMAGE_IO_UTILS_STRING_OUTPUTTER_MESSAGE_WRITER_H_  // NOLINT
diff --git a/src/base/istream_data_source.cc b/src/base/istream_ref_data_source.cc
similarity index 78%
rename from src/base/istream_data_source.cc
rename to src/base/istream_ref_data_source.cc
index d1d66f0..5e3d126 100644
--- a/src/base/istream_data_source.cc
+++ b/src/base/istream_ref_data_source.cc
@@ -1,4 +1,4 @@
-#include "image_io/base/istream_data_source.h"
+#include "image_io/base/istream_ref_data_source.h"
 
 #include "image_io/base/data_destination.h"
 #include "image_io/base/data_segment.h"
@@ -6,13 +6,13 @@
 namespace photos_editing_formats {
 namespace image_io {
 
-void IStreamDataSource::Reset() {
-  istream_->clear();
-  istream_->seekg(0);
+void IStreamRefDataSource::Reset() {
+  istream_ref_.clear();
+  istream_ref_.seekg(0);
   current_data_segment_.reset();
 }
 
-std::shared_ptr<DataSegment> IStreamDataSource::GetDataSegment(
+std::shared_ptr<DataSegment> IStreamRefDataSource::GetDataSegment(
     size_t begin, size_t min_size) {
   if (current_data_segment_ && current_data_segment_->Contains(begin)) {
     return current_data_segment_;
@@ -21,7 +21,7 @@
   return current_data_segment_;
 }
 
-DataSource::TransferDataResult IStreamDataSource::TransferData(
+DataSource::TransferDataResult IStreamRefDataSource::TransferData(
     const DataRange &data_range, size_t best_size,
     DataDestination *data_destination) {
   bool data_transferred = false;
@@ -34,7 +34,7 @@
       status = data_destination->Transfer(data_range, *current_data_segment_);
       data_transferred = true;
     } else {
-      istream_->clear();
+      istream_ref_.clear();
       size_t chunk_size = min_size;
       for (size_t begin = data_range.GetBegin(); begin < data_range.GetEnd();
            begin += chunk_size) {
@@ -63,14 +63,14 @@
   }
 }
 
-std::shared_ptr<DataSegment> IStreamDataSource::Read(size_t begin,
-                                                     size_t count) {
+std::shared_ptr<DataSegment> IStreamRefDataSource::Read(size_t begin,
+                                                        size_t count) {
   std::shared_ptr<DataSegment> shared_data_segment;
-  istream_->seekg(begin);
-  if (istream_->rdstate() == std::ios_base::goodbit) {
+  istream_ref_.seekg(begin);
+  if (istream_ref_.rdstate() == std::ios_base::goodbit) {
     Byte *buffer = new Byte[count];
-    istream_->read(reinterpret_cast<char *>(buffer), count);
-    size_t bytes_read = istream_->gcount();
+    istream_ref_.read(reinterpret_cast<char *>(buffer), count);
+    size_t bytes_read = istream_ref_.gcount();
     shared_data_segment =
         DataSegment::Create(DataRange(begin, begin + bytes_read), buffer);
   }
diff --git a/src/base/message_handler.cc b/src/base/message_handler.cc
index 70959c0..fdacc66 100644
--- a/src/base/message_handler.cc
+++ b/src/base/message_handler.cc
@@ -12,34 +12,10 @@
 using std::string;
 using std::unique_ptr;
 
-/// The message handler. No effort made to delete it at program's end.
-static MessageHandler* gMessageHandler = nullptr;
-
-void MessageHandler::Init(std::unique_ptr<MessageWriter> message_writer,
-                          std::unique_ptr<MessageStore> message_store) {
-  auto* old_handler = gMessageHandler;
-  gMessageHandler = new MessageHandler;
-  gMessageHandler->SetMessageWriter(std::move(message_writer));
-  gMessageHandler->SetMessageStore(std::move(message_store));
-  delete old_handler;
-}
-
-MessageHandler* MessageHandler::Get() {
-  if (!gMessageHandler) {
-    gMessageHandler = new MessageHandler;
-    gMessageHandler->SetMessageWriter(
-        unique_ptr<MessageWriter>(new CoutMessageWriter));
-    gMessageHandler->SetMessageStore(
-        unique_ptr<MessageStore>(new VectorMessageStore));
-  }
-  return gMessageHandler;
-}
-
-MessageHandler::~MessageHandler() {
-  if (gMessageHandler == this) {
-    gMessageHandler = nullptr;
-  }
-}
+MessageHandler::MessageHandler()
+    : message_writer_(new CoutMessageWriter),
+      message_store_(new VectorMessageStore),
+      message_stats_(new MessageStats) {}
 
 void MessageHandler::SetMessageWriter(
     std::unique_ptr<MessageWriter> message_writer) {
@@ -57,6 +33,13 @@
 }
 
 void MessageHandler::ReportMessage(const Message& message) {
+  if (message.IsError()) {
+    message_stats_->error_count++;
+  } else if (message.IsWarning()) {
+    message_stats_->warning_count++;
+  } else {
+    message_stats_->status_count++;
+  }
   if (message_store_) {
     message_store_->AddMessage(message);
   }
diff --git a/src/base/message_writer.cc b/src/base/message_writer.cc
index d13dc41..2f9e0f0 100644
--- a/src/base/message_writer.cc
+++ b/src/base/message_writer.cc
@@ -12,19 +12,37 @@
 
 string MessageWriter::GetFormattedMessage(const Message& message) const {
   stringstream message_stream;
-  message_stream << GetTypeCategory(message.GetType()) << ":"
-                 << GetTypeDescription(message.GetType(),
-                                       message.GetSystemErrno())
-                 << ":" << message.GetText();
+  auto type = message.GetType();
+  if (type != Message::kStatus) {
+    message_stream << GetTypeCategory(type) << ":";
+  }
+  if (type == Message::kInternalError || type == Message::kStdLibError) {
+    message_stream << GetTypeDescription(type, message.GetSystemErrno()) << ":";
+  }
+  message_stream << message.GetText();
   return message_stream.str();
 }
 
 string MessageWriter::GetTypeCategory(Message::Type type) const {
-  if (type == Message::kStatus) {
-    return "STATUS";
-  } else {
-    return "ERROR";
+  string category;
+  switch (type) {
+    case Message::kStatus:
+      category = "STATUS";
+      break;
+    case Message::kWarning:
+      category = "WARNING";
+      break;
+    case Message::kStdLibError:
+    case Message::kPrematureEndOfDataError:
+    case Message::kStringNotFoundError:
+    case Message::kDecodingError:
+    case Message::kSyntaxError:
+    case Message::kValueError:
+    case Message::kInternalError:
+      category = "ERROR";
+      break;
   }
+  return category;
 }
 
 string MessageWriter::GetTypeDescription(Message::Type type,
@@ -33,6 +51,8 @@
   switch (type) {
     case Message::kStatus:
       break;
+    case Message::kWarning:
+      break;
     case Message::kStdLibError:
       description = system_errno > 0 ? std::strerror(system_errno) : "Unknown";
       break;
diff --git a/src/base/ostream_data_destination.cc b/src/base/ostream_data_destination.cc
deleted file mode 100644
index 97915c9..0000000
--- a/src/base/ostream_data_destination.cc
+++ /dev/null
@@ -1,45 +0,0 @@
-#include "image_io/base/ostream_data_destination.h"
-
-#include "image_io/base/data_range.h"
-#include "image_io/base/data_segment.h"
-#include "image_io/base/message_handler.h"
-
-namespace photos_editing_formats {
-namespace image_io {
-
-using std::ostream;
-
-void OStreamDataDestination::StartTransfer() {}
-
-DataDestination::TransferStatus OStreamDataDestination::Transfer(
-    const DataRange& transfer_range, const DataSegment& data_segment) {
-  if (ostream_ && transfer_range.IsValid() && !HasError()) {
-    size_t bytes_written = 0;
-    size_t bytes_to_write = transfer_range.GetLength();
-    const Byte* buffer = data_segment.GetBuffer(transfer_range.GetBegin());
-    if (buffer) {
-      ostream::pos_type prewrite_pos = ostream_->tellp();
-      ostream_->write(reinterpret_cast<const char*>(buffer), bytes_to_write);
-      ostream::pos_type postwrite_pos = ostream_->tellp();
-      if (postwrite_pos != EOF) {
-        bytes_written = ostream_->tellp() - prewrite_pos;
-        bytes_transferred_ += bytes_written;
-      }
-    }
-    if (bytes_written != bytes_to_write) {
-      MessageHandler::Get()->ReportMessage(Message::kStdLibError, name_);
-      has_error_ = true;
-      return kTransferError;
-    }
-  }
-  return kTransferOk;
-}
-
-void OStreamDataDestination::FinishTransfer() {
-  if (ostream_) {
-    ostream_->flush();
-  }
-}
-
-}  // namespace image_io
-}  // namespace photos_editing_formats
diff --git a/src/base/ostream_ref_data_destination.cc b/src/base/ostream_ref_data_destination.cc
new file mode 100644
index 0000000..3658022
--- /dev/null
+++ b/src/base/ostream_ref_data_destination.cc
@@ -0,0 +1,44 @@
+#include "image_io/base/ostream_ref_data_destination.h"
+
+#include "image_io/base/data_range.h"
+#include "image_io/base/data_segment.h"
+
+namespace photos_editing_formats {
+namespace image_io {
+
+using std::ostream;
+
+void OStreamRefDataDestination::StartTransfer() {}
+
+DataDestination::TransferStatus OStreamRefDataDestination::Transfer(
+    const DataRange& transfer_range, const DataSegment& data_segment) {
+  if (transfer_range.IsValid() && !HasError()) {
+    size_t bytes_written = 0;
+    size_t bytes_to_write = transfer_range.GetLength();
+    const Byte* buffer = data_segment.GetBuffer(transfer_range.GetBegin());
+    if (buffer) {
+      ostream::pos_type prewrite_pos = ostream_ref_.tellp();
+      ostream_ref_.write(reinterpret_cast<const char*>(buffer), bytes_to_write);
+      ostream::pos_type postwrite_pos = ostream_ref_.tellp();
+      if (postwrite_pos != EOF) {
+        bytes_written = ostream_ref_.tellp() - prewrite_pos;
+        bytes_transferred_ += bytes_written;
+      }
+    }
+    if (bytes_written != bytes_to_write) {
+      if (message_handler_) {
+        message_handler_->ReportMessage(Message::kStdLibError, name_);
+      }
+      has_error_ = true;
+      return kTransferError;
+    }
+  }
+  return kTransferOk;
+}
+
+void OStreamRefDataDestination::FinishTransfer() {
+    ostream_ref_.flush();
+}
+
+}  // namespace image_io
+}  // namespace photos_editing_formats
diff --git a/src/extras/base64_decoder_data_destination.cc b/src/extras/base64_decoder_data_destination.cc
index a15b997..8ba8bb2 100644
--- a/src/extras/base64_decoder_data_destination.cc
+++ b/src/extras/base64_decoder_data_destination.cc
@@ -127,7 +127,9 @@
                                         decoded_buffer.get(), &pad_count1);
     if (total_bytes_decoded + pad_count1 !=
         number_leftover_and_stolen_decoded_bytes) {
-      MessageHandler::Get()->ReportMessage(Message::kDecodingError, "");
+      if (message_handler_) {
+        message_handler_->ReportMessage(Message::kDecodingError, "");
+      }
       has_error_ = true;
       return kTransferError;
     }
@@ -142,7 +144,9 @@
     total_bytes_decoded += number_bytes_decoded;
     if (total_bytes_decoded + pad_count1 + pad_count2 !=
         decoded_buffer_length) {
-      MessageHandler::Get()->ReportMessage(Message::kDecodingError, "");
+      if (message_handler_) {
+        message_handler_->ReportMessage(Message::kDecodingError, "");
+      }
       has_error_ = true;
       return kTransferError;
     }
@@ -177,7 +181,9 @@
 
 void Base64DecoderDataDestination::FinishTransfer() {
   if (leftover_bytes_.size() % 4) {
-    MessageHandler::Get()->ReportMessage(Message::kDecodingError, "");
+    if (message_handler_) {
+      message_handler_->ReportMessage(Message::kDecodingError, "");
+    }
     has_error_ = true;
   }
   next_destination_->FinishTransfer();
diff --git a/src/gcontainer/gcontainer.cc b/src/gcontainer/gcontainer.cc
index 1179778..b97fdd7 100644
--- a/src/gcontainer/gcontainer.cc
+++ b/src/gcontainer/gcontainer.cc
@@ -4,6 +4,7 @@
 
 #include "image_io/base/data_segment.h"
 #include "image_io/base/data_segment_data_source.h"
+#include "image_io/base/istream_data_source.h"
 #include "image_io/base/message_handler.h"
 #include "image_io/base/ostream_data_destination.h"
 #include "image_io/jpeg/jpeg_info.h"
@@ -19,44 +20,44 @@
 using photos_editing_formats::image_io::DataRange;
 using photos_editing_formats::image_io::DataSegment;
 using photos_editing_formats::image_io::DataSegmentDataSource;
+using photos_editing_formats::image_io::IStreamRefDataSource;
 using photos_editing_formats::image_io::JpegInfoBuilder;
 using photos_editing_formats::image_io::JpegScanner;
 using photos_editing_formats::image_io::Message;
 using photos_editing_formats::image_io::MessageHandler;
 using photos_editing_formats::image_io::OStreamDataDestination;
-using photos_editing_formats::image_io::ReportErrorPolicy;
 using std::string;
 
 // Populates first_image_range with the first image (from the header metadata
 // to the EOI marker) present in the JPEG file input_file_name. Returns true if
 // such a first image is found, false otherwise.
 //
-// input_file_name must be a JPEG file.
+// input_jpeg_stream must be a JPEG stream.
 // image_data_segment is populated with the DataSegment for
 // input_file_name, and is populated only in the successful case.
 // first_image_range is populated with the first image found in the input file,
 // only if such an image is found.
-bool ExtractFirstImageInJpeg(const string& input_file_name,
-                             std::shared_ptr<DataSegment>* image_data_segment,
+
+bool ExtractFirstImageInJpeg(std::istream& input_jpeg_stream,
+                             MessageHandler* message_handler,
                              DataRange* first_image_range) {
   if (first_image_range == nullptr) {
     return false;
   }
 
   // Get the input and output setup.
-  MessageHandler::Get()->ClearMessages();
-  auto data_segment =
-      ReadEntireFile(input_file_name, ReportErrorPolicy::kReportError);
-  if (!data_segment) {
-    return false;
+  if (message_handler) {
+    message_handler->ClearMessages();
   }
 
   // Get the jpeg info and first image range from the input.
-  DataSegmentDataSource data_source(data_segment);
+  IStreamRefDataSource data_source(input_jpeg_stream);
   JpegInfoBuilder jpeg_info_builder;
   jpeg_info_builder.SetImageLimit(1);
-  JpegScanner jpeg_scanner;
+  JpegScanner jpeg_scanner(message_handler);
   jpeg_scanner.Run(&data_source, &jpeg_info_builder);
+  data_source.Reset();
+
   if (jpeg_scanner.HasError()) {
     return false;
   }
@@ -64,12 +65,13 @@
   const auto& jpeg_info = jpeg_info_builder.GetInfo();
   const auto& image_ranges = jpeg_info.GetImageRanges();
   if (image_ranges.empty()) {
-    MessageHandler::Get()->ReportMessage(Message::kPrematureEndOfDataError,
-                                         "No Images Found");
+    if (message_handler) {
+      message_handler->ReportMessage(Message::kPrematureEndOfDataError,
+                                     "No Images Found");
+    }
     return false;
   }
 
-  *image_data_segment = data_segment;
   *first_image_range = image_ranges[0];
   return true;
 }
@@ -79,23 +81,27 @@
 bool WriteImageAndFiles(const string& input_file_name,
                         const std::vector<string>& other_files,
                         const string& output_file_name) {
-  auto output_stream =
-      OpenOutputFile(output_file_name, ReportErrorPolicy::kReportError);
+  MessageHandler message_handler;
+  auto output_stream = OpenOutputFile(output_file_name, &message_handler);
   if (!output_stream) {
     return false;
   }
 
-  OStreamDataDestination output_destination(std::move(output_stream));
+  OStreamDataDestination output_destination(std::move(output_stream),
+                                            &message_handler);
   output_destination.SetName(output_file_name);
 
   DataRange image_range;
-  std::shared_ptr<DataSegment> data_segment;
-  if (!ExtractFirstImageInJpeg(input_file_name, &data_segment, &image_range)) {
+  std::unique_ptr<std::istream> input_stream =
+      OpenInputFile(input_file_name, &message_handler);
+
+  if (!ExtractFirstImageInJpeg(*input_stream, &message_handler, &image_range)) {
     return false;
   }
 
   output_destination.StartTransfer();
-  DataSegmentDataSource data_source(data_segment);
+  IStreamDataSource data_source(
+      OpenInputFile(input_file_name, &message_handler));
   data_source.TransferData(image_range, image_range.GetLength(),
                            &output_destination);
 
@@ -104,8 +110,7 @@
     if (tack_on_file.empty()) {
       continue;
     }
-    auto tack_on_data_segment =
-        ReadEntireFile(tack_on_file, ReportErrorPolicy::kReportError);
+    auto tack_on_data_segment = ReadEntireFile(tack_on_file, &message_handler);
     if (!tack_on_data_segment) {
       continue;
     }
@@ -122,25 +127,40 @@
          !output_destination.HasError();
 }
 
-bool ParseFileAfterImage(const string& input_file_name,
+bool ParseFileAfterImage(const std::string& input_file_name,
                          size_t file_start_offset, size_t file_length,
-                         string* out_file_contents) {
-  if (out_file_contents == nullptr || file_start_offset < 0 ||
-      file_length == 0) {
+                         std::string* out_file_contents) {
+  std::ifstream input_stream(input_file_name);
+  if (!input_stream.is_open()) {
+    return false;
+  }
+  return ParseFileAfterImageFromStream(file_start_offset, file_length,
+                                       input_stream, out_file_contents);
+}
+
+bool ParseFileAfterImageFromStream(size_t start_offset, size_t length,
+                                   std::istream& input_jpeg_stream,
+                                   std::string* out_contents) {
+  if (out_contents == nullptr || start_offset < 0 || length == 0) {
     return false;
   }
 
+  size_t curr_posn = input_jpeg_stream.tellg();
+  input_jpeg_stream.seekg(0, input_jpeg_stream.end);
+  size_t stream_size = input_jpeg_stream.tellg();
+  input_jpeg_stream.seekg(curr_posn, input_jpeg_stream.beg);
+
   DataRange image_range;
-  std::shared_ptr<DataSegment> data_segment;
-  if (!ExtractFirstImageInJpeg(input_file_name, &data_segment, &image_range)) {
+  MessageHandler message_handler;
+  if (!ExtractFirstImageInJpeg(input_jpeg_stream, &message_handler,
+                               &image_range)) {
     return false;
   }
 
   size_t image_bytes_end_offset = image_range.GetEnd();
-  size_t image_file_end = data_segment->GetEnd();
-  size_t file_start_in_image = image_bytes_end_offset + file_start_offset;
-  size_t file_end_in_image = file_start_in_image + file_length;
-  if (image_file_end < file_end_in_image) {
+  size_t file_start_in_image = image_bytes_end_offset + start_offset;
+  size_t file_end_in_image = file_start_in_image + length;
+  if (stream_size < file_end_in_image) {
     // Requested file is past the end of the image file.
     return false;
   }
@@ -151,10 +171,9 @@
   // TODO(miraleung): Consider subclassing image_io/data_destination.h and
   // transferring bytes directly into the string. TBD pending additional mime
   // type getters.
-  std::ifstream input_file_stream(input_file_name);
-  input_file_stream.seekg(file_range.GetBegin());
-  out_file_contents->resize(file_range_size);
-  input_file_stream.read(&(*out_file_contents)[0], file_range_size);
+  input_jpeg_stream.seekg(file_range.GetBegin(), input_jpeg_stream.beg);
+  out_contents->resize(file_range_size);
+  input_jpeg_stream.read(&(*out_contents)[0], file_range_size);
   return true;
 }
 
diff --git a/src/jpeg/jpeg_apple_depth_builder.cc b/src/jpeg/jpeg_apple_depth_builder.cc
index ce83f9f..a45837e 100644
--- a/src/jpeg/jpeg_apple_depth_builder.cc
+++ b/src/jpeg/jpeg_apple_depth_builder.cc
@@ -45,12 +45,14 @@
 /// @param image_limit The limit on the number of images to get info of.
 /// @param data_source The data source from which to get info.
 /// @param info A pointer to the jpeg_info object to receive the info.
+/// @param message_handler For use when reporting messages.
 /// @return Whether the info was obtained successfully or not.
-bool GetJpegInfo(int image_limit, DataSource* data_source, JpegInfo* info) {
+bool GetJpegInfo(int image_limit, DataSource* data_source, JpegInfo* info,
+                 MessageHandler* message_handler) {
   JpegInfoBuilder info_builder;
   info_builder.SetImageLimit(image_limit);
   info_builder.SetCaptureSegmentBytes(kJfif);
-  JpegScanner scanner;
+  JpegScanner scanner(message_handler);
   scanner.Run(data_source, &info_builder);
   if (scanner.HasError()) {
     return false;
@@ -68,13 +70,17 @@
   depth_image_data_source_ = depth_image_data_source;
   data_destination_ = data_destination;
   if (!GetPrimaryImageData()) {
-    MessageHandler::Get()->ReportMessage(Message::kDecodingError,
-                                         "Primary image data");
+    if (message_handler_) {
+      message_handler_->ReportMessage(Message::kDecodingError,
+                                      "Primary image data");
+    }
     return false;
   }
   if (!GetDepthImageData()) {
-    MessageHandler::Get()->ReportMessage(Message::kDecodingError,
-                                         "Depth image data");
+    if (message_handler_) {
+      message_handler_->ReportMessage(Message::kDecodingError,
+                                      "Depth image data");
+    }
     return false;
   }
   data_destination->StartTransfer();
@@ -88,7 +94,7 @@
 
 bool JpegAppleDepthBuilder::GetPrimaryImageData() {
   JpegInfo info;
-  if (!GetJpegInfo(1, primary_image_data_source_, &info)) {
+  if (!GetJpegInfo(1, primary_image_data_source_, &info, message_handler_)) {
     return false;
   }
   if (info.GetImageRanges().empty()) {
@@ -119,7 +125,7 @@
 
 bool JpegAppleDepthBuilder::GetDepthImageData() {
   JpegInfo info;
-  if (!GetJpegInfo(2, depth_image_data_source_, &info)) {
+  if (!GetJpegInfo(2, depth_image_data_source_, &info, message_handler_)) {
     return false;
   }
   if (!info.HasAppleDepth()) {
@@ -239,10 +245,13 @@
         data_destination_->GetBytesTransferred() - old_byte_count;
     if (bytes_transferred != data_range.GetLength()) {
       result = DataSource::kTransferDataError;
-      std::stringstream ss;
-      ss << "JpegAppleDepthBuilder:data source transferred "
-         << bytes_transferred << " bytes instead of " << data_range.GetLength();
-      MessageHandler::Get()->ReportMessage(Message::kInternalError, ss.str());
+      if (message_handler_) {
+        std::stringstream ss;
+        ss << "JpegAppleDepthBuilder:data source transferred "
+           << bytes_transferred << " bytes instead of "
+           << data_range.GetLength();
+        message_handler_->ReportMessage(Message::kInternalError, ss.str());
+      }
     }
   }
   return result == DataSource::kTransferDataSuccess;
diff --git a/src/jpeg/jpeg_image_extractor.cc b/src/jpeg/jpeg_image_extractor.cc
index 82f8fce..b915839 100644
--- a/src/jpeg/jpeg_image_extractor.cc
+++ b/src/jpeg/jpeg_image_extractor.cc
@@ -51,8 +51,9 @@
                data_range_destination.HasDisjointTransferRanges() ||
                data_range_destination.GetTrackedDataRange() != image_range) {
       has_errors = true;
-      MessageHandler::Get()->ReportMessage(Message::kPrematureEndOfDataError,
-                                           "");
+      if (message_handler_) {
+        message_handler_->ReportMessage(Message::kPrematureEndOfDataError, "");
+      }
     }
   }
   data_range_destination.FinishTransfer();
@@ -73,12 +74,13 @@
                                       DataDestination* image_destination) {
   bool has_errors = false;
   const bool has_image = jpeg_info_.HasImage(xmp_info_type);
-  Base64DecoderDataDestination base64_decoder(image_destination);
+  Base64DecoderDataDestination base64_decoder(image_destination,
+                                              message_handler_);
   const vector<DataRange>& data_ranges =
       jpeg_info_.GetSegmentDataRanges(xmp_info_type);
   size_t data_ranges_count = data_ranges.size();
   JpegXmpDataExtractor xmp_data_extractor(xmp_info_type, data_ranges_count,
-                                          &base64_decoder);
+                                          &base64_decoder, message_handler_);
   xmp_data_extractor.StartTransfer();
   if (has_image) {
     for (size_t index = 0; index < data_ranges_count; ++index) {
@@ -97,8 +99,10 @@
         break;
       } else if (result == DataSource::kTransferDataNone) {
         has_errors = true;
-        MessageHandler::Get()->ReportMessage(Message::kPrematureEndOfDataError,
-                                             "");
+        if (message_handler_) {
+          message_handler_->ReportMessage(Message::kPrematureEndOfDataError,
+                                          "");
+        }
       }
     }
   }
diff --git a/src/jpeg/jpeg_scanner.cc b/src/jpeg/jpeg_scanner.cc
index 85426b2..c039f70 100644
--- a/src/jpeg/jpeg_scanner.cc
+++ b/src/jpeg/jpeg_scanner.cc
@@ -93,10 +93,12 @@
   if (next_segment_ && next_segment_->Contains(location)) {
     return next_segment_->GetValidatedByte(location);
   }
-  stringstream sstream;
-  sstream << location;
-  MessageHandler::Get()->ReportMessage(Message::kPrematureEndOfDataError,
-                                       sstream.str());
+  if (message_handler_) {
+    stringstream sstream;
+    sstream << location;
+    message_handler_->ReportMessage(Message::kPrematureEndOfDataError,
+                                    sstream.str());
+  }
   return InvalidByte();
 }
 
diff --git a/src/jpeg/jpeg_xmp_data_extractor.cc b/src/jpeg/jpeg_xmp_data_extractor.cc
index f59dea5..68475f0 100644
--- a/src/jpeg/jpeg_xmp_data_extractor.cc
+++ b/src/jpeg/jpeg_xmp_data_extractor.cc
@@ -50,8 +50,10 @@
       }
     }
     if (xmp_data_begin == encoded_data_begin) {
-      MessageHandler::Get()->ReportMessage(Message::kStringNotFoundError,
-                                           property_name + "=\"");
+      if (message_handler_) {
+        message_handler_->ReportMessage(Message::kStringNotFoundError,
+                                        property_name + "=\"");
+      }
       has_error_ = true;
       return kTransferError;
     }
@@ -59,7 +61,9 @@
   if (segment_index_ == last_segment_index_) {
     xmp_data_end = data_segment.Find(xmp_data_begin, '"');
     if (xmp_data_end == transfer_range.GetEnd()) {
-      MessageHandler::Get()->ReportMessage(Message::kStringNotFoundError, "\"");
+      if (message_handler_) {
+        message_handler_->ReportMessage(Message::kStringNotFoundError, "\"");
+      }
       has_error_ = true;
       return kTransferError;
     }
diff --git a/src/utils/file_utils.cc b/src/utils/file_utils.cc
index d61a2cd..626d537 100644
--- a/src/utils/file_utils.cc
+++ b/src/utils/file_utils.cc
@@ -6,7 +6,6 @@
 #import <memory>
 
 #include "image_io/base/data_range.h"
-#include "image_io/base/message_handler.h"
 
 namespace photos_editing_formats {
 namespace image_io {
@@ -29,38 +28,38 @@
 }
 
 unique_ptr<ostream> OpenOutputFile(const std::string& file_name,
-                                   ReportErrorPolicy report_error_policy) {
+                                   MessageHandler* message_handler) {
   auto* file_stream = new fstream(file_name, std::ios::out | std::ios::binary);
   if (file_stream && !file_stream->is_open()) {
     delete file_stream;
     file_stream = nullptr;
-    if (report_error_policy == ReportErrorPolicy::kReportError) {
-      MessageHandler::Get()->ReportMessage(Message::kStdLibError, file_name);
+    if (message_handler) {
+      message_handler->ReportMessage(Message::kStdLibError, file_name);
     }
   }
   return unique_ptr<ostream>(file_stream);
 }
 
 unique_ptr<istream> OpenInputFile(const std::string& file_name,
-                                  ReportErrorPolicy report_error_policy) {
+                                  MessageHandler* message_handler) {
   auto* file_stream = new fstream(file_name, std::ios::in | std::ios::binary);
   if (file_stream && !file_stream->is_open()) {
     delete file_stream;
     file_stream = nullptr;
-    if (report_error_policy == ReportErrorPolicy::kReportError) {
-      MessageHandler::Get()->ReportMessage(Message::kStdLibError, file_name);
+    if (message_handler) {
+      message_handler->ReportMessage(Message::kStdLibError, file_name);
     }
   }
   return unique_ptr<istream>(file_stream);
 }
 
-std::shared_ptr<DataSegment> ReadEntireFile(
-    const std::string& file_name, ReportErrorPolicy report_error_policy) {
+std::shared_ptr<DataSegment> ReadEntireFile(const std::string& file_name,
+                                            MessageHandler* message_handler) {
   size_t buffer_size = 0;
   std::shared_ptr<DataSegment> shared_data_segment;
   if (GetFileSize(file_name, &buffer_size)) {
     unique_ptr<istream> shared_istream =
-        OpenInputFile(file_name, ReportErrorPolicy::kDontReportError);
+        OpenInputFile(file_name, message_handler);
     if (shared_istream) {
       Byte* buffer = new Byte[buffer_size];
       if (buffer) {
@@ -74,9 +73,8 @@
       }
     }
   }
-  if (!shared_data_segment &&
-      report_error_policy == ReportErrorPolicy::kReportError) {
-    MessageHandler::Get()->ReportMessage(Message::kStdLibError, file_name);
+  if (!shared_data_segment && message_handler) {
+    message_handler->ReportMessage(Message::kStdLibError, file_name);
   }
   return shared_data_segment;
 }