// 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; }