Ejemplo n.º 1
0
Result_t
ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
				 ui32_t sequence, HMACContext* HMAC)
{
  ASDCP_TEST_NULL(AssetID);
  ASDCP_TEST_NULL(HMAC);

  // find the start of the intpack
  byte_t* p = (byte_t*)FB.RoData() + ( FB.Size() - klv_intpack_size );

  // test the AssetID length
  if ( ! Kumu::read_test_BER(&p, UUIDlen) )
        return RESULT_HMACFAIL;

  // test the AssetID
  if ( memcmp(p, AssetID, UUIDlen) != 0 )
    {
      DefaultLogSink().Error("IntegrityPack failure: AssetID mismatch.\n");
      return RESULT_HMACFAIL;
    }
  p += UUIDlen;
  
  // test the sequence length
  if ( ! Kumu::read_test_BER(&p, sizeof(ui64_t)) )
        return RESULT_HMACFAIL;

  ui32_t test_sequence = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(p));

  // test the sequence value
  if ( test_sequence != sequence )
    {
      DefaultLogSink().Error("IntegrityPack failure: sequence is %u, expecting %u.\n", test_sequence, sequence);
      return RESULT_HMACFAIL;
    }

  p += sizeof(ui64_t);

  // test the HMAC length
  if ( ! Kumu::read_test_BER(&p, HMAC_SIZE) )
        return RESULT_HMACFAIL;

  // test the HMAC
  HMAC->Reset();
  HMAC->Update(FB.RoData(), FB.Size() - HMAC_SIZE);
  HMAC->Finalize();

  return HMAC->TestHMACValue(p);
}
Ejemplo n.º 2
0
Result_t
AS_02::MXF::AS02IndexWriterVBR::WriteToFile(Kumu::FileWriter& Writer)
{
  assert(m_Dict);
  ASDCP::FrameBuffer index_body_buffer;
  ui32_t   index_body_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
  Result_t result = index_body_buffer.Capacity(index_body_size); 
  ui64_t start_position = 0;

  if ( m_CurrentSegment != 0 )
    {
      m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
      start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
      m_CurrentSegment = 0;
    }

  std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
  for ( ; pl_i != m_PacketList->m_List.end() && KM_SUCCESS(result); pl_i++ )
    {
      InterchangeObject* object = *pl_i;
      object->m_Lookup = m_Lookup;

      ASDCP::FrameBuffer WriteWrapper;
      WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
			   index_body_buffer.Capacity() - index_body_buffer.Size());
      result = object->WriteToBuffer(WriteWrapper);
      index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
      delete *pl_i;
      *pl_i = 0;
    }

  m_PacketList->m_List.clear();

  if ( KM_SUCCESS(result) )
    {
      IndexByteCount = index_body_buffer.Size();
      UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
      result = Partition::WriteToFile(Writer, body_ul);
    }

  if ( KM_SUCCESS(result) )
    {
      ui32_t write_count = 0;
      result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
      assert(write_count == index_body_buffer.Size());
    }

  if ( KM_SUCCESS(result) )
    {
      m_CurrentSegment = new IndexTableSegment(m_Dict);
      assert(m_CurrentSegment);
      AddChildObject(m_CurrentSegment);
      m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
      m_CurrentSegment->IndexEditRate = m_EditRate;
      m_CurrentSegment->IndexStartPosition = start_position;
    }

  return result;
}
Ejemplo n.º 3
0
static bool
string_is_xml(const ASDCP::FrameBuffer& buffer)
{
  std::string ns_prefix, type_name, namespace_name;
  Kumu::AttributeList doc_attr_list;
  return GetXMLDocType(buffer.RoData(), buffer.Size(),
		       ns_prefix, type_name, namespace_name, doc_attr_list);
}
Ejemplo n.º 4
0
ASDCP::Result_t
ASDCP::KLVPacket::WriteKLToBuffer(ASDCP::FrameBuffer& Buffer, const UL& label, ui32_t length)
{
  assert(label.HasValue());

  if ( Buffer.Size() + kl_length > Buffer.Capacity() )
    {
      DefaultLogSink().Error("Small write buffer\n");
      return RESULT_FAIL;
    }
  
  memcpy(Buffer.Data() + Buffer.Size(), label.Value(), label.Size());

  if ( ! Kumu::write_BER(Buffer.Data() + Buffer.Size() + SMPTE_UL_LENGTH, length, MXF_BER_LENGTH) )
    return RESULT_FAIL;

  Buffer.Size(Buffer.Size() + kl_length);
  return RESULT_OK;
}
Ejemplo n.º 5
0
Result_t
AS_02::h__AS02WriterClip::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
{
  if ( m_ClipStart == 0 )
    {
      DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
      return RESULT_STATE;
    }

  return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
}
Ejemplo n.º 6
0
Result_t
ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
				 ui32_t sequence, HMACContext* HMAC)
{
  ASDCP_TEST_NULL(AssetID);
  ASDCP_TEST_NULL(HMAC);
  byte_t* p = Data;
  HMAC->Reset();

  static byte_t ber_4[MXF_BER_LENGTH] = {0x83, 0, 0, 0};

  // update HMAC with essence data
  HMAC->Update(FB.RoData(), FB.Size());

  // track file ID length
  memcpy(p, ber_4, MXF_BER_LENGTH);
  *(p+3) = UUIDlen;;
  p += MXF_BER_LENGTH;

  // track file ID
  memcpy(p, AssetID, UUIDlen);
  p += UUIDlen;

  // sequence length
  memcpy(p, ber_4, MXF_BER_LENGTH);
  *(p+3) = sizeof(ui64_t);
  p += MXF_BER_LENGTH;

  // sequence number
  Kumu::i2p<ui64_t>(KM_i64_BE(sequence), p);
  p += sizeof(ui64_t);

  // HMAC length
  memcpy(p, ber_4, MXF_BER_LENGTH);
  *(p+3) = HMAC_SIZE;
  p += MXF_BER_LENGTH;

  // update HMAC with intpack values
  HMAC->Update(Data, klv_intpack_size - HMAC_SIZE);

  // finish & write HMAC
  HMAC->Finalize();
  HMAC->GetHMACValue(p);

  assert(p + HMAC_SIZE == Data + klv_intpack_size);

  return RESULT_OK;
}
Ejemplo n.º 7
0
Result_t
AS_02::MXF::AS02IndexWriterCBR::WriteToFile(Kumu::FileWriter& Writer)
{
  assert(m_Dict);
  ASDCP::FrameBuffer index_body_buffer;
  ui32_t   index_body_size = MaxIndexSegmentSize; // segment-count * max-segment-size
  Result_t result = index_body_buffer.Capacity(index_body_size); 

  m_CurrentSegment = new IndexTableSegment(m_Dict);
  assert(m_CurrentSegment);
  m_CurrentSegment->m_Lookup = m_Lookup;
  m_CurrentSegment->IndexEditRate = m_EditRate;
  m_CurrentSegment->IndexStartPosition = 0;
  m_CurrentSegment->IndexDuration = m_Duration;
  m_CurrentSegment->EditUnitByteCount = m_SampleSize;
  AddChildObject(m_CurrentSegment);

  ASDCP::FrameBuffer WriteWrapper;
  WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
		       index_body_buffer.Capacity() - index_body_buffer.Size());

  result = m_CurrentSegment->WriteToBuffer(WriteWrapper);
  index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
  delete m_CurrentSegment;
  m_CurrentSegment = 0;
  m_PacketList->m_List.clear();

  if ( KM_SUCCESS(result) )
    {
      IndexByteCount = index_body_buffer.Size();
      UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
      result = Partition::WriteToFile(Writer, body_ul);
    }

  if ( KM_SUCCESS(result) )
    {
      ui32_t write_count = 0;
      result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
      assert(write_count == index_body_buffer.Size());
    }

  return result;
}
Ejemplo n.º 8
0
// base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
Result_t
ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
			const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
			ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
			const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
{
  KLReader Reader;
  Result_t result = Reader.ReadKLFromFile(File);

  if ( KM_FAILURE(result) )
    return result;

  UL Key(Reader.Key());
  ui64_t PacketLength = Reader.Length();
  LastPosition = LastPosition + Reader.KLLength() + PacketLength;

  if ( Key.MatchIgnoreStream(Dict.ul(MDD_CryptEssence)) )  // ignore the stream numbers
    {
      if ( ! Info.EncryptedEssence )
	{
	  DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
	  return RESULT_FORMAT;
	}

      // read encrypted triplet value into internal buffer
      assert(PacketLength <= 0xFFFFFFFFL);
      CtFrameBuf.Capacity((ui32_t) PacketLength);
      ui32_t read_count;
      result = File.Read(CtFrameBuf.Data(), (ui32_t) PacketLength, &read_count);

      if ( ASDCP_FAILURE(result) )
	return result;

      if ( read_count != PacketLength )
	{
	  DefaultLogSink().Error("read length is smaller than EKLV packet length.\n");
          return RESULT_FORMAT;
        }

      CtFrameBuf.Size((ui32_t) PacketLength);

      // should be const but mxflib::ReadBER is not
      byte_t* ess_p = CtFrameBuf.Data();

      // read context ID length
      if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
	return RESULT_FORMAT;

      // test the context ID
      if ( memcmp(ess_p, Info.ContextID, UUIDlen) != 0 )
	{
	  DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
	  return RESULT_FORMAT;
	}
      ess_p += UUIDlen;

      // read PlaintextOffset length
      if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
	return RESULT_FORMAT;

      ui32_t PlaintextOffset = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
      ess_p += sizeof(ui64_t);

      // read essence UL length
      if ( ! Kumu::read_test_BER(&ess_p, SMPTE_UL_LENGTH) )
	return RESULT_FORMAT;

      // test essence UL
      if ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // ignore the stream number
	{
	  char strbuf[IntBufferLen];
	  const MDDEntry* Entry = Dict.FindUL(Key.Value());

	  if ( Entry == 0 )
	    {
	      DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
	    }
	  else
	    {
	      DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
	    }

	  return RESULT_FORMAT;
	}

      ess_p += SMPTE_UL_LENGTH;

      // read SourceLength length
      if ( ! Kumu::read_test_BER(&ess_p, sizeof(ui64_t)) )
	return RESULT_FORMAT;

      ui32_t SourceLength = (ui32_t)KM_i64_BE(Kumu::cp2i<ui64_t>(ess_p));
      ess_p += sizeof(ui64_t);
      assert(SourceLength);
	  
      if ( FrameBuf.Capacity() < SourceLength )
	{
	  DefaultLogSink().Error("FrameBuf.Capacity: %u SourceLength: %u\n", FrameBuf.Capacity(), SourceLength);
	  return RESULT_SMALLBUF;
	}

      ui32_t esv_length = calc_esv_length(SourceLength, PlaintextOffset);

      // read ESV length
      if ( ! Kumu::read_test_BER(&ess_p, esv_length) )
	{
	  DefaultLogSink().Error("read_test_BER did not return %u\n", esv_length);
	  return RESULT_FORMAT;
	}

      ui32_t tmp_len = esv_length + (Info.UsesHMAC ? klv_intpack_size : 0);

      if ( PacketLength < tmp_len )
	{
	  DefaultLogSink().Error("Frame length is larger than EKLV packet length.\n");
	  return RESULT_FORMAT;
	}

      if ( Ctx )
	{
	  // wrap the pointer and length as a FrameBuffer for use by
	  // DecryptFrameBuffer() and TestValues()
	  FrameBuffer TmpWrapper;
	  TmpWrapper.SetData(ess_p, tmp_len);
	  TmpWrapper.Size(tmp_len);
	  TmpWrapper.SourceLength(SourceLength);
	  TmpWrapper.PlaintextOffset(PlaintextOffset);

	  result = DecryptFrameBuffer(TmpWrapper, FrameBuf, Ctx);
	  FrameBuf.FrameNumber(FrameNum);
  
	  // detect and test integrity pack
	  if ( ASDCP_SUCCESS(result) && Info.UsesHMAC && HMAC )
	    {
	      IntegrityPack IntPack;
	      result = IntPack.TestValues(TmpWrapper, Info.AssetUUID, SequenceNum, HMAC);
	    }
	}
      else // return ciphertext to caller
	{
	  if ( FrameBuf.Capacity() < tmp_len )
	    {
	      char intbuf[IntBufferLen];
	      DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
				     FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
	      return RESULT_SMALLBUF;
	    }

	  memcpy(FrameBuf.Data(), ess_p, tmp_len);
	  FrameBuf.Size(tmp_len);
	  FrameBuf.FrameNumber(FrameNum);
	  FrameBuf.SourceLength(SourceLength);
	  FrameBuf.PlaintextOffset(PlaintextOffset);
	}
    }
  else if ( Key.MatchIgnoreStream(EssenceUL) ) // ignore the stream number
    { // read plaintext frame
       if ( FrameBuf.Capacity() < PacketLength )
	{
	  char intbuf[IntBufferLen];
	  DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %s\n",
				 FrameBuf.Capacity(), ui64sz(PacketLength, intbuf));
	  return RESULT_SMALLBUF;
	}

      // read the data into the supplied buffer
      ui32_t read_count;
      assert(PacketLength <= 0xFFFFFFFFL);
      result = File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
	  
      if ( ASDCP_FAILURE(result) )
	return result;

      if ( read_count != PacketLength )
	{
	  char intbuf1[IntBufferLen];
	  char intbuf2[IntBufferLen];
	  DefaultLogSink().Error("read_count: %s != FrameLength: %s\n",
				 ui64sz(read_count, intbuf1),
				 ui64sz(PacketLength, intbuf2) );
	  
	  return RESULT_READFAIL;
	}

      FrameBuf.FrameNumber(FrameNum);
      FrameBuf.Size(read_count);
    }
  else
    {
      char strbuf[IntBufferLen];
      const MDDEntry* Entry = Dict.FindUL(Key.Value());

      if ( Entry == 0 )
	{
	  DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
	}
      else
	{
	  DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
	}

      return RESULT_FORMAT;
    }

  return result;
}
Ejemplo n.º 9
0
Result_t
ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESDecContext* Ctx)
{
  ASDCP_TEST_NULL(Ctx);
  assert(FBout.Capacity() >= FBin.SourceLength());

  ui32_t ct_size = FBin.SourceLength() - FBin.PlaintextOffset();
  ui32_t diff = ct_size % CBC_BLOCK_SIZE;
  ui32_t block_size = ct_size - diff;
  assert(block_size);
  assert((block_size % CBC_BLOCK_SIZE) == 0);

  const byte_t* buf = FBin.RoData();

  // get ivec
  Ctx->SetIVec(buf);
  buf += CBC_BLOCK_SIZE;

  // decrypt and test check value
  byte_t CheckValue[CBC_BLOCK_SIZE];
  Result_t result = Ctx->DecryptBlock(buf, CheckValue, CBC_BLOCK_SIZE);
  buf += CBC_BLOCK_SIZE;

  if ( memcmp(CheckValue, ESV_CheckValue, CBC_BLOCK_SIZE) != 0 )
    return RESULT_CHECKFAIL;

  // copy plaintext region
  if ( FBin.PlaintextOffset() > 0 )
    {
      memcpy(FBout.Data(), buf, FBin.PlaintextOffset());
      buf += FBin.PlaintextOffset();
    }

  // decrypt all but last block
  if ( ASDCP_SUCCESS(result) )
    {
      result = Ctx->DecryptBlock(buf, FBout.Data() + FBin.PlaintextOffset(), block_size);
      buf += block_size;
    }

  // decrypt last block
  if ( ASDCP_SUCCESS(result) )
    {
      byte_t the_last_block[CBC_BLOCK_SIZE];
      result = Ctx->DecryptBlock(buf, the_last_block, CBC_BLOCK_SIZE);

      if ( the_last_block[diff] != 0 )
	{
	  DefaultLogSink().Error("Unexpected non-zero padding value.\n");
	  return RESULT_FORMAT;
	}

      if ( diff > 0 )
	memcpy(FBout.Data() + FBin.PlaintextOffset() + block_size, the_last_block, diff);
    }

  if ( ASDCP_SUCCESS(result) )
    FBout.Size(FBin.SourceLength());

  return result;
}
Ejemplo n.º 10
0
Result_t
ASDCP::EncryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FBout, AESEncContext* Ctx)
{
  ASDCP_TEST_NULL(Ctx);
  FBout.Size(0);

  // size the buffer
  Result_t result = FBout.Capacity(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));

  // write the IV
  byte_t* p = FBout.Data();

  // write the IV to the frame buffer
  Ctx->GetIVec(p);
  p += CBC_BLOCK_SIZE;


  // encrypt the check value to the frame buffer
  if ( ASDCP_SUCCESS(result) )
    {
      result = Ctx->EncryptBlock(ESV_CheckValue, p, CBC_BLOCK_SIZE);
      p += CBC_BLOCK_SIZE;
    }

  // write optional plaintext region
  if ( FBin.PlaintextOffset() > 0 )
    {
      assert(FBin.PlaintextOffset() <= FBin.Size());
      memcpy(p, FBin.RoData(), FBin.PlaintextOffset());
      p += FBin.PlaintextOffset();
    }

  ui32_t ct_size = FBin.Size() - FBin.PlaintextOffset();
  ui32_t diff = ct_size % CBC_BLOCK_SIZE;
  ui32_t block_size = ct_size - diff;
  assert((block_size % CBC_BLOCK_SIZE) == 0);

  // encrypt the ciphertext region essence data
  if ( ASDCP_SUCCESS(result) )
    {
      result = Ctx->EncryptBlock(FBin.RoData() + FBin.PlaintextOffset(), p, block_size);
      p += block_size;
    }

  // construct and encrypt the padding
  if ( ASDCP_SUCCESS(result) )
    {
      byte_t the_last_block[CBC_BLOCK_SIZE];

      if ( diff > 0 )
	memcpy(the_last_block, FBin.RoData() + FBin.PlaintextOffset() + block_size, diff);

      for (ui32_t i = 0; diff < CBC_BLOCK_SIZE; diff++, i++ )
	the_last_block[diff] = i;

      result = Ctx->EncryptBlock(the_last_block, p, CBC_BLOCK_SIZE);
    }

  if ( ASDCP_SUCCESS(result) )
    FBout.Size(calc_esv_length(FBin.Size(), FBin.PlaintextOffset()));

  return result;
}
Ejemplo n.º 11
0
ASDCP::Result_t
ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type)
{
  type = ESS_UNKNOWN;
  ASDCP::FrameBuffer FB;
  Kumu::FileReader Reader;
  ASDCP::Wav::SimpleWaveHeader WavHeader;
  ASDCP::RF64::SimpleRF64Header RF64Header;
  ASDCP::AIFF::SimpleAIFFHeader AIFFHeader;
  Kumu::XMLElement TmpElement("Tmp");

  ui32_t data_offset;
  ui32_t read_count;
  Result_t result = FB.Capacity(Wav::MaxWavHeader); // using Wav max because everything else is much smaller

  if ( Kumu::PathIsFile(filename) )
    {
      result = Reader.OpenRead(filename);

      if ( ASDCP_SUCCESS(result) )
	{
	  result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
	  Reader.Close();
	}

      if ( ASDCP_SUCCESS(result) )
	{
	  const byte_t* p = FB.RoData();
	  FB.Size(read_count);

	  ui32_t i = 0;
	  while ( p[i] == 0 ) i++;

	  if ( i > 1 && p[i] == 1 &&  (p[i+1] == ASDCP::MPEG2::SEQ_START || p[i+1] == ASDCP::MPEG2::PIC_START) )
	    {
	      type = ESS_MPEG2_VES;
	    }
	  else if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
	    {
	      type = ESS_JPEG_2000;
	    }
	  else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
	    {
	      switch ( WavHeader.samplespersec )
		{
		case 48000: type = ESS_PCM_24b_48k; break;
		case 96000: type = ESS_PCM_24b_96k; break;
		default:
		  return RESULT_FORMAT;
		}
	    }
	  else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
	    {
	      switch ( RF64Header.samplespersec )
		{
		case 48000: type = ESS_PCM_24b_48k; break;
		case 96000: type = ESS_PCM_24b_96k; break;
		default:
		  return RESULT_FORMAT;
		}
	    }
	  else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
	    {
	      type = ESS_PCM_24b_48k;
	    }
	  else if ( string_is_xml(FB) )
	    {
	      type = ESS_TIMED_TEXT;
	    }
	  else if ( ASDCP::ATMOS::IsDolbyAtmos(filename) )
	    {
	      type = ESS_DCDATA_DOLBY_ATMOS;
	    }
	}
    }
  else if ( Kumu::PathIsDirectory(filename) )
    {
      char next_file[Kumu::MaxFilePath];
      Kumu::DirScanner Scanner;
      Result_t result = Scanner.Open(filename);

      if ( ASDCP_SUCCESS(result) )
	{
	  while ( ASDCP_SUCCESS(Scanner.GetNext(next_file)) )
	    {
	      if ( next_file[0] == '.' ) // no hidden files or internal links
		continue;

	      result = Reader.OpenRead(Kumu::PathJoin(filename, next_file));

	      if ( ASDCP_SUCCESS(result) )
		{
		  result = Reader.Read(FB.Data(), FB.Capacity(), &read_count);
		  Reader.Close();
		}

	      if ( ASDCP_SUCCESS(result) )
		{
		  if ( memcmp(FB.RoData(), ASDCP::JP2K::Magic, sizeof(ASDCP::JP2K::Magic)) == 0 )
		    {
		      type = ESS_JPEG_2000;
		    }
		  else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
		    {
		      switch ( WavHeader.samplespersec )
			{
			case 48000: type = ESS_PCM_24b_48k; break;
			case 96000: type = ESS_PCM_24b_96k; break;
			default:
			  return RESULT_FORMAT;
			}
		    }
		  else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
		    {
		      switch ( RF64Header.samplespersec )
			{
			case 48000: type = ESS_PCM_24b_48k; break;
			case 96000: type = ESS_PCM_24b_96k; break;
			default:
			  return RESULT_FORMAT;
			}
		    }
		  else if ( ASDCP::ATMOS::IsDolbyAtmos(Kumu::PathJoin(filename, next_file)) )
		    {
		      type = ESS_DCDATA_DOLBY_ATMOS;
		    }
		  else
		    {
		      type = ESS_DCDATA_UNKNOWN;
		    }
		}
	      
	      break;
	    }
	}
    }

  return result;
}