Example #1
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;
}
ASDCP::Result_t
ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
{
  Result_t result = RESULT_OK;
  ui32_t write_offset = 0;
  ui32_t read_count = 0;

  FB.Size(0);

  if ( m_EOF )
    return RESULT_ENDOFFILE;

  // Data is read in VESReadSize chunks. Each chunk is parsed, and the
  // process is stopped when a Sequence or Picture header is found or when
  // the input file is exhausted. The partial next frame is cached for the
  // next call.
  m_ParserDelegate.Reset();
  m_Parser.Reset();

  if ( m_TmpBuffer.Size() > 0 )
    {
      memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size());
      result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size());
      write_offset = m_TmpBuffer.Size();
      m_TmpBuffer.Size(0);
    }

  while ( ! m_ParserDelegate.m_CompletePicture && result == RESULT_OK )
    {
      if ( FB.Capacity() < ( write_offset + VESReadSize ) )
	{
	  DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %u\n",
				 FB.Capacity(), ( write_offset + VESReadSize ));
	  return RESULT_SMALLBUF;
	}

      result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count);

      if ( result == RESULT_ENDOFFILE || read_count == 0 )
	{
	  m_EOF = true;

	  if ( write_offset > 0 )
	    result = RESULT_OK;
	}

      if ( ASDCP_SUCCESS(result) )
	{
	  result = m_Parser.Parse(FB.RoData() + write_offset, read_count);
	  write_offset += read_count;
	}

      if ( m_EOF )
	break;
    }
  assert(m_ParserDelegate.m_FrameSize <= write_offset);

  if ( ASDCP_SUCCESS(result)
       && m_ParserDelegate.m_FrameSize < write_offset )
    {
      assert(m_TmpBuffer.Size() == 0);
      ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize;
      assert(diff <= m_TmpBuffer.Capacity());

      memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff);
      m_TmpBuffer.Size(diff);
    }

  if ( ASDCP_SUCCESS(result) )
    {
      const byte_t* p = FB.RoData();
      if ( p[0] != 0 || p[1] != 0 || p[2] != 1 || ! ( p[3] == SEQ_START || p[3] == PIC_START ) )
        {
          DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
          return RESULT_RAW_FORMAT;
        }
    }

  if ( ASDCP_SUCCESS(result) )
    {
      FB.Size(m_ParserDelegate.m_FrameSize);
      FB.TemporalOffset(m_ParserDelegate.m_TemporalRef);
      FB.FrameType(m_ParserDelegate.m_FrameType);
      FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset);
      FB.FrameNumber(m_FrameNumber++);
      FB.GOPStart(m_ParserDelegate.m_HasGOP);
      FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP);
    }

  return result;
}