Result_t ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader) { ui32_t read_count; ui32_t header_length = SMPTE_UL_LENGTH + MXF_BER_LENGTH; Result_t result = Reader.Read(m_KeyBuf, header_length, &read_count); if ( ASDCP_FAILURE(result) ) return result; if ( read_count != header_length ) return RESULT_READFAIL; const byte_t* ber_start = m_KeyBuf + SMPTE_UL_LENGTH; if ( ( *ber_start & 0x80 ) == 0 ) { DefaultLogSink().Error("BER encoding error.\n"); return RESULT_FORMAT; } ui8_t ber_size = ( *ber_start & 0x0f ) + 1; if ( ber_size > 9 ) { DefaultLogSink().Error("BER size encoding error.\n"); return RESULT_FORMAT; } if ( ber_size < MXF_BER_LENGTH ) { DefaultLogSink().Error("BER size %d shorter than AS-DCP/AS-02 minimum %d.\n", ber_size, MXF_BER_LENGTH); return RESULT_FORMAT; } if ( ber_size > MXF_BER_LENGTH ) { ui32_t diff = ber_size - MXF_BER_LENGTH; assert((SMPTE_UL_LENGTH + MXF_BER_LENGTH + diff) <= (SMPTE_UL_LENGTH * 2)); result = Reader.Read(m_KeyBuf + SMPTE_UL_LENGTH + MXF_BER_LENGTH, diff, &read_count); if ( ASDCP_FAILURE(result) ) return result; if ( read_count != diff ) return RESULT_READFAIL; header_length += diff; } return InitFromBuffer(m_KeyBuf, header_length); }
Result_t OpenReadFrame(const char* filename, FrameBuffer& FB) { ASDCP_TEST_NULL_STR(filename); m_File.Close(); Result_t result = m_File.OpenRead(filename); if ( ASDCP_SUCCESS(result) ) { Kumu::fsize_t file_size = m_File.Size(); if ( FB.Capacity() < file_size ) { DefaultLogSink().Error("FrameBuf.Capacity: %u frame length: %u\n", FB.Capacity(), (ui32_t)file_size); return RESULT_SMALLBUF; } } ui32_t read_count; if ( ASDCP_SUCCESS(result) ) result = m_File.Read(FB.Data(), FB.Capacity(), &read_count); if ( ASDCP_SUCCESS(result) ) FB.Size(read_count); return result; }
ASDCP::Result_t ASDCP::AIFF::SimpleAIFFHeader::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start) { ui32_t read_count = 0; ui32_t local_data_start = 0; ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader); if ( data_start == 0 ) data_start = &local_data_start; Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count); if ( ASDCP_SUCCESS(result) ) result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start); return result; }
ASDCP::Result_t ASDCP::RF64::SimpleRF64Header::ReadFromFile(const Kumu::FileReader& InFile, ui32_t* data_start) { ui32_t read_count = 0; ui32_t local_data_start = 0; ASDCP::PCM::FrameBuffer TmpBuffer(Wav::MaxWavHeader); if ( data_start == 0 ) data_start = &local_data_start; Result_t result = InFile.Read(TmpBuffer.Data(), TmpBuffer.Capacity(), &read_count); if ( ASDCP_SUCCESS(result) ) result = ReadFromBuffer(TmpBuffer.RoData(), read_count, data_start); else DefaultLogSink().Error("Failed to read %d bytes from file\n", Wav::MaxWavHeader); return result; }
// 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::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; }
Result_t AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence) { typedef std::list<Kumu::mem_ptr<ASDCP::MXF::Partition> > body_part_array_t; body_part_array_t body_part_array; body_part_array_t::const_iterator body_part_iter; ASDCP::MXF::Array<ASDCP::MXF::RIP::Pair>::const_iterator i; Result_t result = m_IndexSegmentData.Capacity(128*Kumu::Kilobyte); // will be grown if needed ui32_t first_body_sid = 0; // create a list of body parts and index parts for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i ) { if ( i->BodySID == 0 ) continue; if ( first_body_sid == 0 ) { first_body_sid = i->BodySID; } else if ( i->BodySID != first_body_sid ) { DefaultLogSink().Debug("The index assembler is ignoring BodySID %d.\n", i->BodySID); continue; } reader.Seek(i->ByteOffset); ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict); assert(this_partition); result = this_partition->InitFromFile(reader); if ( KM_FAILURE(result) ) { delete this_partition; return result; } if ( this_partition->BodySID != i->BodySID ) { DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n", this_partition->BodySID, i->BodySID); } body_part_array.push_back(0); body_part_array.back().set(this_partition); } if ( body_part_array.empty() ) { DefaultLogSink().Error("File has no partitions with essence data.\n"); return RESULT_AS02_FORMAT; } body_part_iter = body_part_array.begin(); for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i ) { reader.Seek(i->ByteOffset); ASDCP::MXF::Partition plain_part(m_Dict); result = plain_part.InitFromFile(reader); if ( KM_FAILURE(result) ) return result; if ( plain_part.IndexByteCount > 0 ) { if ( body_part_iter == body_part_array.end() ) { DefaultLogSink().Error("Index and Body partitions do not match.\n"); break; } if ( plain_part.ThisPartition == plain_part.FooterPartition ) { DefaultLogSink().Warn("File footer partition contains index data.\n"); } // slurp up the remainder of the partition ui32_t read_count = 0; assert (plain_part.IndexByteCount <= 0xFFFFFFFFL); ui32_t bytes_this_partition = (ui32_t)plain_part.IndexByteCount; result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition); if ( KM_SUCCESS(result) ) result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(), bytes_this_partition, &read_count); if ( KM_SUCCESS(result) && read_count != bytes_this_partition ) { DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n", read_count, bytes_this_partition); return RESULT_AS02_FORMAT; } if ( KM_SUCCESS(result) ) { ui64_t current_body_offset = 0; ui64_t current_ec_offset = 0; assert(body_part_iter != body_part_array.end()); assert(!body_part_iter->empty()); ASDCP::MXF::Partition *tmp_partition = body_part_iter->get(); if ( has_header_essence && tmp_partition->ThisPartition == 0 ) { current_body_offset = 0; current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize(); } else { current_body_offset = tmp_partition->BodyOffset; current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize(); } result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition, current_body_offset, current_ec_offset); m_IndexSegmentData.Length(m_IndexSegmentData.Length() + bytes_this_partition); ++body_part_iter; } } } if ( KM_SUCCESS(result) ) { std::list<InterchangeObject*>::const_iterator ii; for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii ) { IndexTableSegment *segment = dynamic_cast<IndexTableSegment*>(*ii); if ( segment != 0 ) { m_Duration += segment->IndexDuration; } } } #if 0 char identbuf[IdentBufferLen]; std::list<InterchangeObject*>::iterator j; std::vector<ASDCP::MXF::IndexTableSegment::IndexEntry>::iterator k; ui32_t entry_count = 0; for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j ) { assert(*j); ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j); fprintf(stderr, " --------------------------------------\n"); fprintf(stderr, " IndexEditRate = %d/%d\n", segment->IndexEditRate.Numerator, segment->IndexEditRate.Denominator); fprintf(stderr, " IndexStartPosition = %s\n", i64sz(segment->IndexStartPosition, identbuf)); fprintf(stderr, " IndexDuration = %s\n", i64sz(segment->IndexDuration, identbuf)); fprintf(stderr, " EditUnitByteCount = %u\n", segment->EditUnitByteCount); fprintf(stderr, " IndexSID = %u\n", segment->IndexSID); fprintf(stderr, " BodySID = %u\n", segment->BodySID); fprintf(stderr, " SliceCount = %hu\n", segment->SliceCount); fprintf(stderr, " PosTableCount = %hu\n", segment->PosTableCount); fprintf(stderr, " RtFileOffset = %s\n", i64sz(segment->RtFileOffset, identbuf)); fprintf(stderr, " RtEntryOffset = %s\n", i64sz(segment->RtEntryOffset, identbuf)); fprintf(stderr, " IndexEntryArray:\n"); for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k ) { fprintf(stderr, " 0x%010qx\n", k->StreamOffset); ++entry_count; } } fprintf(stderr, "Actual entries: %d\n", entry_count); #endif return result; }
// TODO: refactor to use InitFromBuffer ASDCP::Result_t ASDCP::KLVFilePacket::InitFromFile(const Kumu::FileReader& Reader) { ui32_t read_count; byte_t tmp_data[tmp_read_size]; ui64_t tmp_size; m_KeyStart = m_ValueStart = 0; m_KLLength = m_ValueLength = 0; m_Buffer.Size(0); Result_t result = Reader.Read(tmp_data, tmp_read_size, &read_count); if ( ASDCP_FAILURE(result) ) return result; if ( read_count < (SMPTE_UL_LENGTH + 1) ) { DefaultLogSink().Error("Short read of Key and Length got %u\n", read_count); return RESULT_READFAIL; } if ( memcmp(tmp_data, SMPTE_UL_START, 4) != 0 ) { DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n", tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); return RESULT_FAIL; } if ( ! Kumu::read_BER(tmp_data + SMPTE_UL_LENGTH, &tmp_size) ) { DefaultLogSink().Error("BER Length decoding error\n"); return RESULT_FAIL; } if ( tmp_size > MAX_KLV_PACKET_LENGTH ) { Kumu::ui64Printer tmp_size_str(tmp_size); DefaultLogSink().Error("Packet length %s exceeds internal limit\n", tmp_size_str.c_str()); return RESULT_FAIL; } ui32_t remainder = 0; ui32_t ber_len = Kumu::BER_length(tmp_data + SMPTE_UL_LENGTH); m_KLLength = SMPTE_UL_LENGTH + ber_len; assert(tmp_size <= 0xFFFFFFFFL); m_ValueLength = (ui32_t) tmp_size; ui32_t packet_length = m_ValueLength + m_KLLength; result = m_Buffer.Capacity(packet_length); if ( ASDCP_FAILURE(result) ) return result; m_KeyStart = m_Buffer.Data(); m_ValueStart = m_Buffer.Data() + m_KLLength; m_Buffer.Size(packet_length); // is the whole packet in the tmp buf? if ( packet_length <= tmp_read_size ) { assert(packet_length <= read_count); memcpy(m_Buffer.Data(), tmp_data, packet_length); if ( (remainder = read_count - packet_length) != 0 ) { DefaultLogSink().Warn("Repositioning pointer for short packet\n"); Kumu::fpos_t pos = Reader.Tell(); assert(pos > remainder); result = Reader.Seek(pos - remainder); } } else { if ( read_count < tmp_read_size ) { DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n", m_Buffer.Size(), read_count); return RESULT_READFAIL; } memcpy(m_Buffer.Data(), tmp_data, tmp_read_size); remainder = m_Buffer.Size() - tmp_read_size; if ( remainder > 0 ) { result = Reader.Read(m_Buffer.Data() + tmp_read_size, remainder, &read_count); if ( read_count != remainder ) { DefaultLogSink().Error("Short read of packet body, expecting %u, got %u\n", remainder+tmp_read_size, read_count+tmp_read_size); result = RESULT_READFAIL; } } } return result; }