ASDCP::Result_t ASDCP::MPEG2::Parser::h__Parser::OpenRead(const std::string& filename) { ui32_t read_count = 0; Result_t result = m_FileReader.OpenRead(filename); if ( ASDCP_SUCCESS(result) ) result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count); if ( ASDCP_SUCCESS(result) ) { const byte_t* p = m_TmpBuffer.RoData(); // the mxflib parser demanded the file start with a sequence header. // Since no one complained and that's the easiest thing to implement, // I have left it that way. Let me know if you want to be able to // locate the first GOP in the stream. ui32_t i = 0; while ( p[i] == 0 ) i++; if ( i < 2 || p[i] != 1 || ! ( p[i+1] == SEQ_START || p[i+1] == 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) ) { m_Parser.SetDelegate(&m_ParamsDelegate); result = m_Parser.Parse(p, read_count); } } if ( ASDCP_SUCCESS(result) ) { ui64_t tmp = m_FileReader.Size() / 65536; // a gross approximation m_ParamsDelegate.m_VDesc.ContainerDuration = (ui32_t) tmp; m_Parser.SetDelegate(&m_ParserDelegate); m_FileReader.Seek(0); } if ( ASDCP_FAILURE(result) ) { DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename.c_str()); m_FileReader.Close(); } return result;}
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; }
bool ASDCP::MXF::TLVReader::FindTL(const MDDEntry& Entry) { if ( m_Lookup == 0 ) { DefaultLogSink().Error("No Lookup service\n"); return false; } TagValue TmpTag; if ( m_Lookup->TagForKey(Entry.ul, TmpTag) != RESULT_OK ) { if ( Entry.tag.a == 0 ) { // DefaultLogSink().Debug("No such UL in this TL list: %s (%02x %02x)\n", // Entry.name, Entry.tag.a, Entry.tag.b); return false; } TmpTag = Entry.tag; } TagMap::iterator e_i = m_ElementMap.find(TmpTag); if ( e_i != m_ElementMap.end() ) { m_size = (*e_i).second.first; m_capacity = m_size + (*e_i).second.second; return true; } // DefaultLogSink().Debug("Not Found (%02x %02x): %s\n", TmpTag.a, TmpTag.b, Entry.name); return false; }
bool ASDCP::MXF::UTF16String::Unarchive(Kumu::MemIOReader* Reader) { erase(); const ui16_t* p = (ui16_t*)Reader->CurrentData(); ui32_t length = Reader->Remainder() / 2; char mb_buf[MB_LEN_MAX+1]; for ( ui32_t i = 0; i < length; i++ ) { int count = wctomb(mb_buf, KM_i16_BE(p[i])); if ( count == -1 ) { DefaultLogSink().Error("Unable to decode wide character 0x%04hx\n", p[i]); return false; } assert(count <= MB_LEN_MAX); mb_buf[count] = 0; this->append(mb_buf); } Reader->SkipOffset(length*2); return true; }
void print_ssl_error() { char err_buf[256]; unsigned long errval = ERR_get_error(); DefaultLogSink().Error("OpenSSL: %s\n", ERR_error_string(errval, err_buf)); }
Result_t ASDCP::TimedText::DCSubtitleParser::h__SubtitleParser::ReadAncillaryResource(const byte_t* uuid, FrameBuffer& FrameBuf, const IResourceResolver& Resolver) const { FrameBuf.AssetID(uuid); UUID TmpID(uuid); char buf[64]; ResourceTypeMap_t::const_iterator rmi = m_ResourceTypes.find(TmpID); if ( rmi == m_ResourceTypes.end() ) { DefaultLogSink().Error("Unknown ancillary resource id: %s\n", TmpID.EncodeHex(buf, 64)); return RESULT_RANGE; } Result_t result = Resolver.ResolveRID(uuid, FrameBuf); if ( KM_SUCCESS(result) ) { if ( (*rmi).second == MT_PNG ) FrameBuf.MIMEType("image/png"); else if ( (*rmi).second == MT_OPENTYPE ) FrameBuf.MIMEType("application/x-font-opentype"); else FrameBuf.MIMEType("application/octet-stream"); } return result; }
// Open the file for writing. The file must not exist. Returns error if // the operation cannot be completed. Result_t AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor, ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const AS_02::IndexStrategy_t& IndexStrategy, const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize) { if ( ! m_State.Test_BEGIN() ) { KM_RESULT_STATE_HERE(); return RESULT_STATE; } if ( m_IndexStrategy != AS_02::IS_FOLLOW ) { DefaultLogSink().Error("Only strategy IS_FOLLOW is supported at this time.\n"); return Kumu::RESULT_NOTIMPL; } Result_t result = m_File.OpenWrite(filename.c_str()); if ( KM_SUCCESS(result) ) { m_IndexStrategy = IndexStrategy; m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream() m_HeaderSize = HeaderSize; if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor)) && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) ) { DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n"); essence_descriptor->Dump(); return RESULT_AS02_FORMAT; } m_EssenceDescriptor = essence_descriptor; ASDCP::MXF::InterchangeObject_list_t::iterator i; for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i ) { if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) ) { DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n"); (*i)->Dump(); } m_EssenceSubDescriptorList.push_back(*i); GenRandomValue((*i)->InstanceUID); m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID); *i = 0; // parent will only free the ones we don't keep } result = m_State.Goto_INIT(); } return result; }
bool ASDCP::MXF::UTF16String::Archive(Kumu::MemIOWriter* Writer) const { if ( size() > IdentBufferLen ) { DefaultLogSink().Error("String length exceeds maximum %u bytes\n", IdentBufferLen); return false; } const char* mbp = c_str(); wchar_t wcp; ui32_t remainder = size(); ui32_t length = size(); ui32_t i = 0; while ( i < length ) { int count = mbtowc(&wcp, mbp+i, remainder); if ( count == -1 ) { DefaultLogSink().Error("Error decoding multi-byte sequence starting at offset %u\n", i); return false; } else if ( count == 0 ) { break; } bool result = Writer->WriteUi16BE((ui16_t)wcp); if ( result == false ) { DefaultLogSink().Error("No more space in memory IO writer\n"); return false; } i += count; remainder -= count; } return true; }
bool ASDCP::MXF::ISO8String::Archive(Kumu::MemIOWriter* Writer) const { if ( size() > IdentBufferLen ) { DefaultLogSink().Error("String length exceeds maximum %u bytes\n", IdentBufferLen); return false; } return Writer->WriteRaw((const byte_t*)c_str(), size()); }
ASDCP::Result_t AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc) { if ( ! m_State.Test_INIT() ) { KM_RESULT_STATE_HERE(); return RESULT_STATE; } assert(m_Dict); m_TDesc = TDesc; Result_t result = TimedText_TDesc_to_MD(m_TDesc); if ( KM_SUCCESS(result) ) { ResourceList_t::const_iterator i; for ( i = m_TDesc.ResourceList.begin() ; i != m_TDesc.ResourceList.end(); ++i ) { TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict); GenRandomValue(resourceSubdescriptor->InstanceUID); resourceSubdescriptor->AncillaryResourceID.Set((*i).ResourceID); resourceSubdescriptor->MIMEMediaType = MIME2str((*i).Type); resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++; m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor); m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID); // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72; } } if ( KM_SUCCESS(result) ) { result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)), "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_TimedTextEssence)), TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate)); } if ( KM_SUCCESS(result) ) { this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer); } if ( KM_SUCCESS(result) ) { memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH); m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container result = m_State.Goto_READY(); } return result; }
ASDCP::Result_t ASDCP::MXF::TLVWriter::WriteTag(const MDDEntry& Entry) { if ( m_Lookup == 0 ) { DefaultLogSink().Error("No Primer object available\n"); return RESULT_FAIL; } TagValue TmpTag; if ( m_Lookup->InsertTag(Entry, TmpTag) != RESULT_OK ) { DefaultLogSink().Error("No tag for entry %s\n", Entry.name); return RESULT_FAIL; } if ( ! MemIOWriter::WriteUi8(TmpTag.a) ) return RESULT_KLV_CODING(__LINE__, __FILE__); if ( ! MemIOWriter::WriteUi8(TmpTag.b) ) return RESULT_KLV_CODING(__LINE__, __FILE__); return RESULT_OK; }
inline Result_t Goto_SEQ() { switch ( m_State ) { case ST_INIT: case ST_EXT: m_State = ST_SEQ; return RESULT_OK; } DefaultLogSink().Error("SEQ follows %s\n", StringParserState(m_State)); return RESULT_STATE; }
inline Result_t Goto_SLICE() { switch ( m_State ) { case ST_PIC: case ST_EXT: m_State = ST_SLICE; return RESULT_OK; } DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State)); return RESULT_STATE; }
Result_t ASDCP::TimedText::LocalFilenameResolver::OpenRead(const std::string& dirname) { if ( PathIsDirectory(dirname) ) { m_Dirname = dirname; return RESULT_OK; } DefaultLogSink().Error("Path '%s' is not a directory, defaulting to '.'\n", dirname.c_str()); m_Dirname = "."; return RESULT_FALSE; }
ASDCP::Result_t ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len) { m_KeyStart = m_ValueStart = 0; m_KLLength = m_ValueLength = 0; if ( memcmp(buf, SMPTE_UL_START, 4) != 0 ) { DefaultLogSink().Error("Unexpected UL preamble: %02x.%02x.%02x.%02x\n", buf[0], buf[1], buf[2], buf[3]); return RESULT_FAIL; } ui32_t ber_len = Kumu::BER_length(buf + SMPTE_UL_LENGTH); if ( ber_len > ( buf_len - SMPTE_UL_LENGTH ) ) { DefaultLogSink().Error("BER encoding length exceeds buffer size\n"); return RESULT_FAIL; } if ( ber_len == 0 ) { DefaultLogSink().Error("KLV format error, zero BER length not allowed\n"); return RESULT_FAIL; } ui64_t tmp_size; if ( ! Kumu::read_BER(buf + SMPTE_UL_LENGTH, &tmp_size) ) return RESULT_FAIL; assert (tmp_size <= 0xFFFFFFFFL); m_ValueLength = (ui32_t) tmp_size; m_KLLength = SMPTE_UL_LENGTH + Kumu::BER_length(buf + SMPTE_UL_LENGTH); m_KeyStart = buf; m_ValueStart = buf + m_KLLength; return RESULT_OK; }
ASDCP::Result_t ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc) { if ( ! m_State.Test_INIT() ) return RESULT_STATE; m_TDesc = TDesc; ResourceList_t::const_iterator ri; Result_t result = TimedText_TDesc_to_MD(m_TDesc); for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ ) { TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict); GenRandomValue(resourceSubdescriptor->InstanceUID); resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID); resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type); resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++; m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor); m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID); // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72; } m_EssenceStreamID = 10; assert(m_Dict); if ( ASDCP_SUCCESS(result) ) { InitHeader(); AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL, UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL); AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping))); result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize); if ( KM_SUCCESS(result) ) result = CreateBodyPart(m_TDesc.EditRate); } if ( ASDCP_SUCCESS(result) ) { memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH); m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container result = m_State.Goto_READY(); } return result; }
Result_t ASDCP::TimedText::LocalFilenameResolver::ResolveRID(const byte_t* uuid, TimedText::FrameBuffer& FrameBuf) const { Result_t result = RESULT_NOT_FOUND; char buf[64]; UUID RID(uuid); PathList_t found_list; FindInPath(PathMatchRegex(RID.EncodeHex(buf, 64)), m_Dirname, found_list); if ( found_list.size() == 1 ) { FileReader Reader; DefaultLogSink().Debug("retrieving resource %s from file %s\n", buf, found_list.front().c_str()); result = Reader.OpenRead(found_list.front().c_str()); if ( KM_SUCCESS(result) ) { ui32_t read_count, read_size = Reader.Size(); result = FrameBuf.Capacity(read_size); if ( KM_SUCCESS(result) ) result = Reader.Read(FrameBuf.Data(), read_size, &read_count); if ( KM_SUCCESS(result) ) FrameBuf.Size(read_count); } } else if ( ! found_list.empty() ) { DefaultLogSink().Error("More than one file in %s matches %s.\n", m_Dirname.c_str(), buf); result = RESULT_RAW_FORMAT; } return result; }
inline Result_t Goto_EXT() { switch ( m_State ) { case ST_PIC: case ST_EXT: case ST_SEQ: case ST_GOP: m_State = ST_EXT; return RESULT_OK; } DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State)); return RESULT_STATE; }
inline Result_t Goto_GOP() { switch ( m_State ) { case ST_EXT: case ST_SEQ: m_State = ST_GOP; return RESULT_OK; default: break; } DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State)); return RESULT_STATE; }
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; }
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; }
ASDCP::MXF::TLVReader::TLVReader(const byte_t* p, ui32_t c, IPrimerLookup* PrimerLookup) : MemIOReader(p, c), m_Lookup(PrimerLookup) { Result_t result = RESULT_OK; while ( Remainder() > 0 && ASDCP_SUCCESS(result) ) { TagValue Tag; ui16_t pkt_len = 0; if ( MemIOReader::ReadUi8(&Tag.a) ) if ( MemIOReader::ReadUi8(&Tag.b) ) if ( MemIOReader::ReadUi16BE(&pkt_len) ) { m_ElementMap.insert(TagMap::value_type(Tag, ItemInfo(m_size, pkt_len))); if ( SkipOffset(pkt_len) ) continue;; } DefaultLogSink().Error("Malformed Set\n"); m_ElementMap.clear(); result = RESULT_KLV_CODING(__LINE__, __FILE__); } }
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; }
// all the above for a single source clip Result_t AS_02::PHDR::MXFWriter::h__Writer::WritePHDRHeader(const std::string& PackageLabel, const ASDCP::UL& WrappingUL, const std::string& TrackName, const ASDCP::UL& EssenceUL, const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate, const ui32_t& TCFrameRate) { if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 ) { DefaultLogSink().Error("Non-zero edit-rate reqired.\n"); return RESULT_PARAM; } InitHeader(); AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel); // add metadata track TrackSet<SourceClip> metdata_track = CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage, MD_DEF_LABEL, EditRate, UL(m_Dict->ul(MDD_PHDRImageMetadataItem)), 3 /* track id */, m_Dict); metdata_track.Sequence->Duration.set_has_value(); m_DurationUpdateList.push_back(&(metdata_track.Sequence->Duration.get())); // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from. metdata_track.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((UL(m_MetadataUL).Value() + 12))); metdata_track.Clip = new SourceClip(m_Dict); m_HeaderPart.AddChildObject(metdata_track.Clip); metdata_track.Sequence->StructuralComponents.push_back(metdata_track.Clip->InstanceUID); metdata_track.Clip->DataDefinition = UL(m_Dict->ul(MDD_PHDRImageMetadataWrappingFrame)); // for now we do not allow setting this value, so all files will be 'original' metdata_track.Clip->SourceTrackID = 0; metdata_track.Clip->SourcePackageID = NilUMID; metdata_track.Clip->Duration.set_has_value(); m_DurationUpdateList.push_back(&(metdata_track.Clip->Duration.get())); // add PHDR subdescriptor m_MetadataTrackSubDescriptor = new PHDRMetadataTrackSubDescriptor(m_Dict); m_EssenceSubDescriptorList.push_back(m_MetadataTrackSubDescriptor); GenRandomValue(m_MetadataTrackSubDescriptor->InstanceUID); m_EssenceDescriptor->SubDescriptors.push_back(m_MetadataTrackSubDescriptor->InstanceUID); m_MetadataTrackSubDescriptor->DataDefinition = UL(m_Dict->ul(MDD_PHDRImageMetadataWrappingFrame)); m_MetadataTrackSubDescriptor->SourceTrackID = 3; m_MetadataTrackSubDescriptor->SimplePayloadSID = 0; AddEssenceDescriptor(WrappingUL); m_IndexWriter.SetPrimerLookup(&m_HeaderPart.m_Primer); m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // Header partition RIP entry m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern; m_IndexWriter.EssenceContainers = m_HeaderPart.EssenceContainers; Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize); if ( KM_SUCCESS(result) ) { m_PartitionSpace *= floor( EditRate.Quotient() + 0.5 ); // convert seconds to edit units m_ECStart = m_File.Tell(); m_IndexWriter.IndexSID = 129; UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition)); Partition body_part(m_Dict); body_part.BodySID = 1; body_part.OperationalPattern = m_HeaderPart.OperationalPattern; body_part.EssenceContainers = m_HeaderPart.EssenceContainers; body_part.ThisPartition = m_ECStart; result = body_part.WriteToFile(m_File, body_ul); m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition)); // Second RIP Entry } return result; }
ASDCP::Result_t ASDCP::RF64::SimpleRF64Header::WriteToFile(Kumu::FileWriter& OutFile) const { static ui32_t fmt_len = sizeof(format) + sizeof(nchannels) + sizeof(samplespersec) + sizeof(avgbps) + sizeof(blockalign) + sizeof(bitspersample) + sizeof(cbsize); ui32_t write_count = 0; ui64_t RIFF_len = data_len + SimpleWavHeaderLength - 8; // DefaultLogSink().Debug("RIFF_len is %llu.\n", RIFF_len); byte_t* tmp_header = NULL; ui32_t header_len = 0; if (RIFF_len > MAX_RIFF_LEN) { DefaultLogSink().Debug("Will write out an RF64 wave file.\n"); ui32_t data32_len = ((data_len < MAX_RIFF_LEN) ? data_len : MAX_RIFF_LEN); ui64_t data64_len = ((data_len < MAX_RIFF_LEN) ? 0 : data_len); static ui32_t ds64_len = sizeof(RIFF_len) + sizeof(data64_len) + sizeof(SAMPLE_COUNT) + sizeof(TABLE_LEN); header_len = SIMPLE_RF64_HEADER_LEN; tmp_header = new byte_t[header_len]; byte_t* p = tmp_header; memcpy(p, &FCC_RF64, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(MAX_RIFF_LEN); p += 4; memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4; memcpy(p, &FCC_ds64, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(ds64_len); p += 4; *((ui64_t*)p) = KM_i64_LE(RIFF_len); p += 8; *((ui64_t*)p) = KM_i64_LE(data64_len); p += 8; *((ui64_t*)p) = KM_i64_LE(SAMPLE_COUNT); p += 8; *((ui32_t*)p) = KM_i32_LE(TABLE_LEN); p += 4; memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4; *((ui16_t*)p) = KM_i16_LE(format); p += 2; *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2; *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4; *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4; *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2; *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2; *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2; memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(data32_len); p += 4; write_count = (p - tmp_header); } else { DefaultLogSink().Debug("Will write out a regular wave file.\n"); header_len = SimpleWavHeaderLength; tmp_header = new byte_t[header_len]; byte_t* p = tmp_header; memcpy(p, &Wav::FCC_RIFF, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(RIFF_len); p += 4; memcpy(p, &Wav::FCC_WAVE, sizeof(fourcc)); p += 4; memcpy(p, &Wav::FCC_fmt_, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(fmt_len); p += 4; *((ui16_t*)p) = KM_i16_LE(format); p += 2; *((ui16_t*)p) = KM_i16_LE(nchannels); p += 2; *((ui32_t*)p) = KM_i32_LE(samplespersec); p += 4; *((ui32_t*)p) = KM_i32_LE(avgbps); p += 4; *((ui16_t*)p) = KM_i16_LE(blockalign); p += 2; *((ui16_t*)p) = KM_i16_LE(bitspersample); p += 2; *((ui16_t*)p) = KM_i16_LE(cbsize); p += 2; memcpy(p, &Wav::FCC_data, sizeof(fourcc)); p += 4; *((ui32_t*)p) = KM_i32_LE(data_len); p += 4; write_count = (p - tmp_header); } if (header_len != write_count) { DefaultLogSink().Warn("Expected to write %u bytes but wrote %u bytes for header.\n", header_len, write_count); } write_count = 0; ASDCP::Result_t r = OutFile.Write(tmp_header, header_len, &write_count); delete [] tmp_header; return r; }
ASDCP::Result_t ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start) { if ( buf_len < SimpleWavHeaderLength ) return RESULT_SMALLBUF; *data_start = 0; const byte_t* p = buf; const byte_t* end_p = p + buf_len; fourcc test_RIFF(p); p += 4; if ( test_RIFF != FCC_RIFF ) { // DefaultLogSink().Debug("File does not begin with RIFF header\n"); return RESULT_RAW_FORMAT; } ui32_t RIFF_len = KM_i32_LE(*(ui32_t*)p); p += 4; fourcc test_WAVE(p); p += 4; if ( test_WAVE != FCC_WAVE ) { DefaultLogSink().Debug("File does not contain a WAVE header\n"); return RESULT_RAW_FORMAT; } fourcc test_fcc; while ( p < end_p ) { test_fcc = fourcc(p); p += 4; ui32_t chunk_size = KM_i32_LE(*(ui32_t*)p); p += 4; if ( test_fcc == FCC_data ) { if ( chunk_size > RIFF_len ) { DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len); return RESULT_RAW_FORMAT; } data_len = chunk_size; *data_start = p - buf; break; } if ( test_fcc == FCC_fmt_ ) { ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2; if ( format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_EXTENSIBLE ) { DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format); return RESULT_RAW_FORMAT; } nchannels = KM_i16_LE(*(ui16_t*)p); p += 2; samplespersec = KM_i32_LE(*(ui32_t*)p); p += 4; avgbps = KM_i32_LE(*(ui32_t*)p); p += 4; blockalign = KM_i16_LE(*(ui16_t*)p); p += 2; bitspersample = KM_i16_LE(*(ui16_t*)p); p += 2; p += chunk_size - 16; // 16 is the number of bytes read in this block } else { p += chunk_size; } } if ( *data_start == 0 ) // can't have no data! { DefaultLogSink().Error("No data chunk found, file contains no essence\n"); return RESULT_RAW_FORMAT; } return RESULT_OK; }
ASDCP::Result_t ASDCP::AIFF::SimpleAIFFHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, ui32_t* data_start) { if ( buf_len < 32 ) return RESULT_SMALLBUF; *data_start = 0; const byte_t* p = buf; const byte_t* end_p = p + buf_len; fourcc test_FORM(p); p += 4; if ( test_FORM != FCC_FORM ) { // DefaultLogSink().Debug("File does not begin with FORM header\n"); return RESULT_RAW_FORMAT; } ui32_t RIFF_len = KM_i32_BE(*(ui32_t*)p); p += 4; fourcc test_AIFF(p); p += 4; if ( test_AIFF != FCC_AIFF ) { DefaultLogSink().Debug("File does not contain an AIFF header\n"); return RESULT_RAW_FORMAT; } fourcc test_fcc; while ( p < end_p ) { test_fcc = fourcc(p); p += 4; ui32_t chunk_size = KM_i32_BE(*(ui32_t*)p); p += 4; if ( test_fcc == FCC_COMM ) { numChannels = KM_i16_BE(*(ui16_t*)p); p += 2; numSampleFrames = KM_i32_BE(*(ui32_t*)p); p += 4; sampleSize = KM_i16_BE(*(ui16_t*)p); p += 2; memcpy(sampleRate, p, 10); p += 10; } else if ( test_fcc == FCC_SSND ) { if ( chunk_size > RIFF_len ) { DefaultLogSink().Error("Chunk size %u larger than file: %u\n", chunk_size, RIFF_len); return RESULT_RAW_FORMAT; } ui32_t offset = KM_i32_BE(*(ui32_t*)p); p += 4; p += 4; // blockSize; data_len = chunk_size - 8; *data_start = (p - buf) + offset; break; } else { p += chunk_size; } } if ( *data_start == 0 ) // can't have no data! { DefaultLogSink().Error("No data chunk found, file contains no essence\n"); return RESULT_RAW_FORMAT; } return RESULT_OK; }
ASDCP::Result_t ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc) { if ( ! m_State.Test_INIT() ) return RESULT_STATE; m_TDesc = TDesc; ResourceList_t::const_iterator ri; Result_t result = TimedText_TDesc_to_MD(m_TDesc); for ( ri = m_TDesc.ResourceList.begin() ; ri != m_TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ ) { TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict); GenRandomValue(resourceSubdescriptor->InstanceUID); resourceSubdescriptor->AncillaryResourceID.Set((*ri).ResourceID); resourceSubdescriptor->MIMEMediaType = MIME2str((*ri).Type); resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++; m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor); m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID); // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72; } m_EssenceStreamID = 10; assert(m_Dict); if ( ASDCP_SUCCESS(result) ) { InitHeader(MXFVersion_2004); // First RIP Entry if ( m_Info.LabelSetType == LS_MXF_SMPTE ) // ERK { m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header } else { DefaultLogSink().Error("Unable to write Interop timed-text MXF file. Use SMPTE DCP options instead.\n"); return RESULT_FORMAT; } // timecode rate and essence rate are the same AddSourceClip(m_TDesc.EditRate, m_TDesc.EditRate, derive_timecode_rate_from_edit_rate(m_TDesc.EditRate), TIMED_TEXT_DEF_LABEL, m_EssenceUL, UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL); AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrappingClip))); result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize); if ( KM_SUCCESS(result) ) result = CreateBodyPart(m_TDesc.EditRate); } if ( ASDCP_SUCCESS(result) ) { memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH); m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container result = m_State.Goto_READY(); } return result; }
Result_t ASDCP::TimedText::DCSubtitleParser::h__SubtitleParser::OpenRead() { if ( ! m_Root.ParseString(m_XMLDoc.c_str()) ) return RESULT_FORMAT; m_TDesc.EncodingName = "UTF-8"; // the XML parser demands UTF-8 m_TDesc.ResourceList.clear(); m_TDesc.ContainerDuration = 0; const XMLNamespace* ns = m_Root.Namespace(); if ( ns == 0 ) { DefaultLogSink(). Warn("Document has no namespace name, assuming %s\n", c_dcst_namespace_name); m_TDesc.NamespaceName = c_dcst_namespace_name; } else { m_TDesc.NamespaceName = ns->Name(); } UUID DocID; if ( ! get_UUID_from_child_element("Id", &m_Root, DocID) ) { DefaultLogSink(). Error("Id element missing from input document\n"); return RESULT_FORMAT; } memcpy(m_TDesc.AssetID, DocID.Value(), DocID.Size()); XMLElement* EditRate = m_Root.GetChildWithName("EditRate"); if ( EditRate == 0 ) { DefaultLogSink(). Error("EditRate element missing from input document\n"); return RESULT_FORMAT; } m_TDesc.EditRate = decode_rational(EditRate->GetBody().c_str()); if ( m_TDesc.EditRate != EditRate_23_98 && m_TDesc.EditRate != EditRate_24 && m_TDesc.EditRate != EditRate_25 && m_TDesc.EditRate != EditRate_30 && m_TDesc.EditRate != EditRate_48 && m_TDesc.EditRate != EditRate_50 && m_TDesc.EditRate != EditRate_60 ) { DefaultLogSink(). Error("Unexpected EditRate: %d/%d\n", m_TDesc.EditRate.Numerator, m_TDesc.EditRate.Denominator); return RESULT_FORMAT; } // list of fonts ElementList FontList; m_Root.GetChildrenWithName("LoadFont", FontList); for ( Elem_i i = FontList.begin(); i != FontList.end(); i++ ) { UUID AssetID; if ( ! get_UUID_from_element(*i, AssetID) ) { DefaultLogSink(). Error("LoadFont element does not contain a urn:uuid value as expected.\n"); return RESULT_FORMAT; } TimedTextResourceDescriptor TmpResource; memcpy(TmpResource.ResourceID, AssetID.Value(), UUIDlen); TmpResource.Type = MT_OPENTYPE; m_TDesc.ResourceList.push_back(TmpResource); m_ResourceTypes.insert(ResourceTypeMap_t::value_type(UUID(TmpResource.ResourceID), MT_OPENTYPE)); } // list of images ElementList ImageList; m_Root.GetChildrenWithName("Image", ImageList); std::set<Kumu::UUID> visited_items; for ( Elem_i i = ImageList.begin(); i != ImageList.end(); i++ ) { UUID AssetID; if ( ! get_UUID_from_element(*i, AssetID) ) { DefaultLogSink(). Error("Image element does not contain a urn:uuid value as expected.\n"); return RESULT_FORMAT; } if ( visited_items.find(AssetID) == visited_items.end() ) { TimedTextResourceDescriptor TmpResource; memcpy(TmpResource.ResourceID, AssetID.Value(), UUIDlen); TmpResource.Type = MT_PNG; m_TDesc.ResourceList.push_back(TmpResource); m_ResourceTypes.insert(ResourceTypeMap_t::value_type(UUID(TmpResource.ResourceID), MT_PNG)); visited_items.insert(AssetID); } } // Calculate the timeline duration. // This is a little ugly because the last element in the file is not necessarily // the last instance to be displayed, e.g., element n and element n-1 may have the // same start time but n-1 may have a greater duration making it the last to be seen. // We must scan the list to accumulate the latest TimeOut value. ElementList InstanceList; ElementList::const_iterator ei; ui32_t end_count = 0; m_Root.GetChildrenWithName("Subtitle", InstanceList); if ( InstanceList.empty() ) { DefaultLogSink(). Error("XML document contains no Subtitle elements.\n"); return RESULT_FORMAT; } // assumes edit rate is constrained above ui32_t TCFrameRate = ( m_TDesc.EditRate == EditRate_23_98 ) ? 24 : m_TDesc.EditRate.Numerator; S12MTimecode beginTC; beginTC.SetFPS(TCFrameRate); XMLElement* StartTime = m_Root.GetChildWithName("StartTime"); if ( StartTime != 0 ) beginTC.DecodeString(StartTime->GetBody()); for ( ei = InstanceList.begin(); ei != InstanceList.end(); ei++ ) { S12MTimecode tmpTC((*ei)->GetAttrWithName("TimeOut"), TCFrameRate); if ( end_count < tmpTC.GetFrames() ) end_count = tmpTC.GetFrames(); } if ( end_count <= beginTC.GetFrames() ) { DefaultLogSink(). Error("Timed Text file has zero-length timeline.\n"); return RESULT_FORMAT; } m_TDesc.ContainerDuration = end_count - beginTC.GetFrames(); return RESULT_OK; }
// 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; }