diff --git a/DDDigi/include/DDDigi/DigiInputAction.h b/DDDigi/include/DDDigi/DigiInputAction.h
index acf5d75f88cfc4ab6c7b640d7aece68ce1def373..c5b1373be96df61542c1ac94bd1e81920b3cbd4c 100644
--- a/DDDigi/include/DDDigi/DigiInputAction.h
+++ b/DDDigi/include/DDDigi/DigiInputAction.h
@@ -42,6 +42,18 @@ namespace dd4hep {
       /// Most probably need some minimum cutoff:
       static constexpr double epsilon = std::numeric_limits<double>::epsilon();
 
+      /// Input source base
+      class input_source   {
+      public:
+	/// Event counter for current file
+	int event_count  { 0 };
+      };
+
+      /// Event frame base
+      class event_frame   {
+      public:
+      };
+
     protected:
       /// Property: Input data specification
       std::vector<std::string> m_input_sources { };
@@ -55,9 +67,13 @@ namespace dd4hep {
       std::string              m_input_segment { "inputs" };
       /// Property: Mask to flag input source items
       int                      m_input_mask    { NO_MASK };
+      /// Property: Number of events to be read by file
+      int                      m_events_per_file { -1 };
       /// Property: Loop on inputs and restart at end
       bool                     m_input_rescan { true };
-
+      /// Property: generate raw input history records to Digi store
+      bool                     m_keep_raw       { true };
+      
     protected:
       /// Define standard assignments and constructors
       DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiInputAction);
@@ -92,6 +108,13 @@ namespace dd4hep {
       const std::vector<std::string>& objects_disabled()  const   {
 	return m_objects_disabled;
       }
+      /// Check if the number of events per file is reached
+      bool fileLimitReached(input_source& source)   const;
+      /// Callback when a new file is opened
+      virtual void onOpenFile(input_source& source);
+      /// Callback when a new event is processed
+      virtual void onProcessEvent(input_source& source, event_frame& frame);
+
       /// Check if a event object should be loaded: Default YES unless inhibited by selection or veto
       bool object_loading_is_enabled(const std::string& nam)  const;
 
diff --git a/DDDigi/io/DigiDDG4Input.cpp b/DDDigi/io/DigiDDG4Input.cpp
index 54bf785afc0bc804cd0ba3a38ed8fdc58f22fcf2..88a1e1ae34884a1269c842f685cf40b21e837be2 100644
--- a/DDDigi/io/DigiDDG4Input.cpp
+++ b/DDDigi/io/DigiDDG4Input.cpp
@@ -33,9 +33,6 @@ namespace dd4hep {
 
     class DigiDDG4ROOT : public DigiROOTInput    {
     public:
-      /// Property to generate extra history records
-      bool    m_keep_raw        { true };
-
       /// Class pointers of the objects to be imported
       TClass* m_trackerHitClass { nullptr };
       TClass* m_caloHitClass    { nullptr };
@@ -44,7 +41,6 @@ namespace dd4hep {
     public:
       /// Initializing constructor
       DigiDDG4ROOT(const DigiKernel& krnl, const std::string& nam) : DigiROOTInput(krnl, nam)      {
-	declareProperty("keep_raw", m_keep_raw);
 	m_particlesClass  = gROOT->GetClass(typeid(std::vector<dd4hep::sim::Geant4Particle*>), kTRUE);
 	m_trackerHitClass = gROOT->GetClass(typeid(std::vector<dd4hep::sim::Geant4Tracker::Hit*>), kTRUE);
 	m_caloHitClass    = gROOT->GetClass(typeid(std::vector<dd4hep::sim::Geant4Calorimeter::Hit*>), kTRUE);
diff --git a/DDDigi/io/DigiEdm4hepInput.cpp b/DDDigi/io/DigiEdm4hepInput.cpp
index 1663fd1fee786749414a2920374cd275061508a2..ee06258c687353cfa614b2f3e5fdc1d3f63fa849 100644
--- a/DDDigi/io/DigiEdm4hepInput.cpp
+++ b/DDDigi/io/DigiEdm4hepInput.cpp
@@ -54,9 +54,7 @@ namespace dd4hep {
       std::string m_caloHitType    { };
       /// Type of particles container
       std::string m_particlesType  { };
-      /// Property to generate extra history records
-      bool        m_keep_raw       { true };
-
+      
     public:
       /// Initializing constructor
       DigiEdm4hepInput(const DigiKernel& krnl, const std::string& nam);
@@ -120,9 +118,14 @@ namespace dd4hep {
   /// Namespace for the Digitization part of the AIDA detector description toolkit
   namespace digi {
 
+    class edm4hep_read_frame_t : public DigiInputAction::event_frame   {
+    public:
+      podio::Frame frame { };
+      edm4hep_read_frame_t(podio::Frame&& frm) : frame(std::move(frm)) {}
+    };
     using reader_t = podio::ROOTFrameReader;
-    using frame_t  = podio::Frame;
-
+    using frame_t  = edm4hep_read_frame_t;
+    
     /// EDM4HEP Digi input reader: Collection descriptor definition
     /**
      *
@@ -159,7 +162,7 @@ namespace dd4hep {
      *  \version 1.0
      *  \ingroup DD4HEP_DIGITIZATION
      */
-    class DigiEdm4hepInput::inputsource_t   {
+    class DigiEdm4hepInput::inputsource_t : public DigiInputAction::input_source   {
     public:
       /// Reference to the reader object
       std::unique_ptr<reader_t>   stream      { };
@@ -217,7 +220,6 @@ namespace dd4hep {
       input_t           m_source { };
       /// Pointer to current input source
       int               m_curr_input   { INPUT_START };
-      std::size_t       m_curr_event   { 0 };
 
     public:
       /// Initializing constructor
@@ -247,6 +249,7 @@ namespace dd4hep {
 	  stream->openFile(fname);
 	  auto source = std::make_unique<inputsource_t>(sec, std::move(stream));
 	  m_parent->info("+++ Opened EDM4HEP input file %s.", fname.c_str());
+	  m_parent->onOpenFile(*source);
 	  return source;
 	}
 	catch (const std::runtime_error& e)   {
@@ -258,13 +261,13 @@ namespace dd4hep {
     }
 
     std::shared_ptr<frame_t> DigiEdm4hepInput::internals_t::next()   {
-      if ( !m_source || m_source->done() )    {
+      if ( !m_source || m_source->done() || m_parent->fileLimitReached(*m_source) )    {
 	int mask = m_parent->input_mask();
 	m_source = open_next_data_source();
 	if ( m_source )   {
 	  auto frame = m_source->next();
 	  if ( frame )   {
-	    auto table = frame->getIDTable();
+	    auto table = frame->frame.getIDTable();
 	    const auto& ids = table.ids();
 	    for( int id : ids )   {
 	      std::string nam = table.name(id);
@@ -274,13 +277,16 @@ namespace dd4hep {
 		m_source->collections.emplace( key, collection_t(id, nam) );
 	      }
 	    }
+	    m_parent->onProcessEvent(*m_source, *frame);
 	    return frame;
 	  }
 	  m_parent->except("+++ No valid frame present in file.");
 	}
 	m_parent->except("+++ No open file present. Aborting processing");
       }
-      return m_source->next();
+      auto frame = m_source->next();
+      m_parent->onProcessEvent(*m_source, *frame);
+      return frame;
     }
 
     /// Initializing constructor
@@ -291,7 +297,6 @@ namespace dd4hep {
       m_trackerHitType = typeName(typeid(edm4hep::SimTrackerHitCollection));
       m_caloHitType    = typeName(typeid(edm4hep::SimCalorimeterHitCollection));
       m_particlesType  = typeName(typeid(edm4hep::MCParticleCollection));
-      declareProperty("keep_raw",              m_keep_raw);
       declareProperty("tracker_hits_type",     m_trackerHitType);
       declareProperty("calorimeter_hits_type", m_caloHitType);
       declareProperty("particles_hits_type",   m_particlesType);
@@ -308,7 +313,7 @@ namespace dd4hep {
 
       for( auto& coll : internals->m_source->collections )    {
 	const auto& nam = coll.second.name;
-	const podio::CollectionBase* collection = frame->get(nam);
+	const podio::CollectionBase* collection = frame->frame.get(nam);
 	if ( collection )   {
 	  work_t work { context, coll, segment, collection };
 	  (*this)(context, work);
diff --git a/DDDigi/src/DigiInputAction.cpp b/DDDigi/src/DigiInputAction.cpp
index 4a5d6358493964e5680c10a2ff49b0757f361f47..87561b45d656e693df44db36441aeaaaad608b85 100644
--- a/DDDigi/src/DigiInputAction.cpp
+++ b/DDDigi/src/DigiInputAction.cpp
@@ -11,19 +11,18 @@
 //
 //==========================================================================
 
-// Framework include files
+/// Framework include files
 #include <DD4hep/InstanceCount.h>
 #include <DDDigi/DigiInputAction.h>
 
-// C/C++ include files
+/// C/C++ include files
 #include <stdexcept>
 #include <unistd.h>
 
-using namespace std;
 using namespace dd4hep::digi;
 
 /// Standard constructor
-DigiInputAction::DigiInputAction(const DigiKernel& kernel, const string& nam)
+DigiInputAction::DigiInputAction(const DigiKernel& kernel, const std::string& nam)
   : DigiEventAction(kernel, nam)
 {
   declareProperty("input",            m_input_sources);
@@ -33,6 +32,8 @@ DigiInputAction::DigiInputAction(const DigiKernel& kernel, const string& nam)
   declareProperty("input_section",    m_input_section);
   declareProperty("objects_enabled",  m_objects_enabled);
   declareProperty("objects_disabled", m_objects_disabled);
+  declareProperty("events_per_file",  m_events_per_file);
+  declareProperty("keep_raw",         m_keep_raw);
   InstanceCount::increment(this);
 }
 
@@ -41,6 +42,26 @@ DigiInputAction::~DigiInputAction()   {
   InstanceCount::decrement(this);
 }
 
+/// Check if the number of events per file is reached
+bool DigiInputAction::fileLimitReached(input_source& source)   const    {
+  if ( m_events_per_file > 0 )    {
+    if ( source.event_count > m_events_per_file )  {
+      return true;
+    }
+  }
+  return false;
+}
+
+/// Callback when a new file is opened
+void DigiInputAction::onOpenFile(input_source& source)    {
+  source.event_count = 0;
+}
+
+/// Callback when a new event is processed
+void DigiInputAction::onProcessEvent(input_source& source, event_frame& /* frame */)   {
+  ++source.event_count = 0;
+}
+
 /// Check if a event object should be loaded: Default YES unless inhibited by selection or veto
 bool DigiInputAction::object_loading_is_enabled(const std::string& nam)  const   {
   /// If there are no required branches, we convert everything
diff --git a/DDDigi/src/DigiROOTInput.cpp b/DDDigi/src/DigiROOTInput.cpp
index 5d64df5569775d745944f6938ce8e1775a6c7f9b..b7bc81b8dcee2a56d0a27d3bbf01990cb056838d 100644
--- a/DDDigi/src/DigiROOTInput.cpp
+++ b/DDDigi/src/DigiROOTInput.cpp
@@ -28,16 +28,19 @@
 
 using namespace dd4hep::digi;
 
-class DigiROOTInput::inputsource_t   {
+class DigiROOTInput::inputsource_t
+  : public DigiInputAction::input_source,
+    public DigiInputAction::event_frame
+{
 public:
   /// Branches present in the current file
   std::map<Key, container_t>  branches  { };
   /// Reference to the current ROOT file to be read
-  TFile*     file   { nullptr };
+  TFile*   file   { nullptr };
   /// Reference to the ROOT tree to be read
-  TTree*     tree   { nullptr };
+  TTree*   tree   { nullptr };
   /// Current entry inside the current input source
-  Long64_t   entry  { -1 };
+  Long64_t entry  { -1 };
 
 public:
   inputsource_t() = default;
@@ -66,11 +69,11 @@ class DigiROOTInput::internals_t   {
 public:
   using handle_t = std::unique_ptr<inputsource_t>;
   /// Reference to parent action
-  DigiROOTInput* m_parent  { nullptr };
+  DigiROOTInput* m_parent       { nullptr };
   /// Handle to input source
-  handle_t       m_input_handle;
+  handle_t       m_source       { };
   /// Pointer to current input source
-  int            m_curr_input  { INPUT_START };
+  int            m_curr_input   { INPUT_START };
 
 public:
   /// Default constructor
@@ -89,24 +92,12 @@ DigiROOTInput::internals_t::internals_t (DigiROOTInput* p)
 {
 }
 
-DigiROOTInput::inputsource_t& DigiROOTInput::internals_t::next()   {
-  if ( !m_input_handle )    {
-    m_input_handle = open_next_data_source();
-  }
-  Int_t total = m_input_handle->tree->GetEntries();
-  if ( (1+m_input_handle->entry) >= total )   {
-    m_input_handle.reset();
-    m_input_handle = open_next_data_source();
-  }
-  return m_input_handle->next();
-}
-
 std::unique_ptr<DigiROOTInput::inputsource_t>
 DigiROOTInput::internals_t::open_next_data_source()   {
-  const auto& inputs = m_parent->inputs();
+  const auto& inputs    = m_parent->inputs();
   const auto& tree_name = m_parent->input_section();
-
   int len = inputs.size();
+
   if ( inputs.empty() ) m_curr_input = 0;
   while ( (m_curr_input+1) < len )   {
     const auto& fname = inputs[++m_curr_input];
@@ -149,6 +140,7 @@ DigiROOTInput::internals_t::open_next_data_source()   {
       if ( source->branches.empty() )    {
 	m_parent->except("+++ No branches to be loaded. Configuration error!");
       }
+      m_parent->onOpenFile(*source);
       return source;
     }
   }
@@ -156,6 +148,20 @@ DigiROOTInput::internals_t::open_next_data_source()   {
   throw std::runtime_error("+++ No open file present");
 }
 
+DigiROOTInput::inputsource_t& DigiROOTInput::internals_t::next()   {
+  if ( !m_source || m_parent->fileLimitReached(*m_source) )    {
+    m_source = open_next_data_source();
+  }
+  Int_t total = m_source->tree->GetEntries();
+  if ( (1+m_source->entry) >= total )   {
+    m_source.reset();
+    m_source = open_next_data_source();
+  }
+  auto& src = m_source->next();
+  m_parent->onProcessEvent(src, src);
+  return src;
+}
+
 /// Standard constructor
 DigiROOTInput::DigiROOTInput(const DigiKernel& kernel, const std::string& nam)
   : DigiInputAction(kernel, nam)
diff --git a/examples/DDDigi/scripts/TestEdm4hepOutput.py b/examples/DDDigi/scripts/TestEdm4hepOutput.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b0fafcf45b805ef7cbe52da358cb709766967df
--- /dev/null
+++ b/examples/DDDigi/scripts/TestEdm4hepOutput.py
@@ -0,0 +1,38 @@
+# ==========================================================================
+#  AIDA Detector description implementation
+# --------------------------------------------------------------------------
+# Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+# All rights reserved.
+#
+# For the licensing terms see $DD4hepINSTALL/LICENSE.
+# For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+#
+# ==========================================================================
+from __future__ import absolute_import
+
+
+# ---------------------------------------------------------------------------
+def run():
+  import DigiTest
+  digi = DigiTest.Test(geometry=None)
+  read = digi.input_action('DigiDDG4ROOT/SignalReader', mask=0x0, input=[digi.next_input()])
+  dump = digi.event_action('DigiStoreDump/StoreDump', parallel=False)
+  writ = digi.output_action('Digi2edm4hepWriter/Writer',
+                            parallel=True,
+                            input_mask=0x0,
+                            input_segment='input',
+                            output='MiniTel_DDDigi_edm4hep_data.root')
+  proc = digi.create_action('Digi2edm4hepProcessor/edm4hep')
+  hit_type = 'TrackerHits'
+  if digi.hit_type:
+    hit_type = digi.hit_type
+  cont = [c + '/' + hit_type for c in digi.containers()]
+  writ.adopt_container_processor(proc, cont)
+  writ.adopt_container_processor(proc, 'MCParticles/MCParticles')
+  digi.check_creation([read, dump])
+  digi.run_checked(num_events=10, num_threads=10, parallel=3)
+
+
+# ---------------------------------------------------------------------------
+if __name__ == '__main__':
+  run()