diff --git a/IsoLib/t35_tool/extraction/MebxMe4cExtractor.cpp b/IsoLib/t35_tool/extraction/MebxMe4cExtractor.cpp index 3b08e73..f12800f 100644 --- a/IsoLib/t35_tool/extraction/MebxMe4cExtractor.cpp +++ b/IsoLib/t35_tool/extraction/MebxMe4cExtractor.cpp @@ -213,41 +213,70 @@ static MP4Err findMebxMe4cTrackReader(MP4Movie moov, const std::string &t35Prefi if(setupInfoSize > 0) { - // Parse setupInfo binary format: - // 1. utf8string description (null-terminated) - // 2. unsigned int(8) t35_identifier[] (remaining bytes) - - char *setupData = (char *)*setupInfoH; - - // Read null-terminated description - size_t descLen = 0; - for(size_t i = 0; i < setupInfoSize; i++) + // Simulate the box size and 4CC type, and the reserved and dataReferenceIndex fields + // to reuse ISOGetT35SampleEntryFields() and ISOGetFirstHumanReadableStreamDescription() + u32 it35SampleEntryHeaderSize = 4 + 4 + 6 + 2; + u32 it35SampleEntrySize = it35SampleEntryHeaderSize + setupInfoSize; + MP4Handle it35SampleEntryH = nullptr; + err = MP4NewHandle(it35SampleEntryHeaderSize, &it35SampleEntryH); + if(err) { - if(setupData[i] == '\0') - { - descLen = i; - break; - } + LOG_ERROR("Failed to create it35SampleEntryH handle (err={})", err); + MP4DisposeHandle(setupInfoH); + MP4DisposeHandle(read_key_value); + continue; } - - std::string desc; - if(descLen > 0) + char *it35SampleEntryData = (char *)*it35SampleEntryH; + it35SampleEntryData[0] = (it35SampleEntrySize >> 24) & 0xFF; // box size + it35SampleEntryData[1] = (it35SampleEntrySize >> 16) & 0xFF; + it35SampleEntryData[2] = (it35SampleEntrySize >> 8) & 0xFF; + it35SampleEntryData[3] = (it35SampleEntrySize >> 0) & 0xFF; + memcpy(&it35SampleEntryData[4], keyPtr, 4); // 'it35' + memset(&it35SampleEntryData[4 + 4], 0, 6); // reserved + memset(&it35SampleEntryData[4 + 4 + 6], 0, 2); // dataReferenceIndex + err = MP4HandleCat(it35SampleEntryH, setupInfoH); // unsigned int(8) t35_identifier_length + // unsigned int(8) t35_identifier[] + // Optional hrsd box + if(err) { - desc = std::string(setupData, descLen); + LOG_ERROR("Failed to write to it35SampleEntryH handle (err={})", err); + MP4DisposeHandle(it35SampleEntryH); + MP4DisposeHandle(setupInfoH); + MP4DisposeHandle(read_key_value); + continue; } - // Read remaining bytes as t35_identifier - u32 identifierStart = descLen + 1; // Skip null terminator - u32 identifierSize = setupInfoSize - identifierStart; + u8 *identifier = nullptr; + u32 identifierLength = 0; + char *description = NULL; + MP4Err readErr = + ISOGetT35SampleEntryFields(it35SampleEntryH, &identifier, &identifierLength); + MP4Err hrsdErr = ISOGetFirstHumanReadableStreamDescription(it35SampleEntryH, &description); + + MP4DisposeHandle(it35SampleEntryH); std::vector identifierBytes; - if(identifierSize > 0 && identifierStart < setupInfoSize) + std::string desc; + + if(readErr) + { + LOG_WARN("Could not read t35_identifier from mebx me4c it35"); + } + else { - identifierBytes.assign((uint8_t *)(setupData + identifierStart), - (uint8_t *)(setupData + setupInfoSize)); + identifierBytes.assign(identifier, identifier + identifierLength); + if(hrsdErr) + { + LOG_WARN("Could not read HumanReadableStreamDescription"); + } + else if(description) + { + desc = description; + } } + free(identifier); + free(description); - // Convert identifier bytes to hex string std::string hexStr = T35Prefix::bytesToHex(identifierBytes); LOG_DEBUG(" Parsed setupInfo: description='{}', identifier={} ({} bytes)", diff --git a/IsoLib/t35_tool/injection/MebxMe4cStrategy.cpp b/IsoLib/t35_tool/injection/MebxMe4cStrategy.cpp index 39b5afa..9dfbdb5 100644 --- a/IsoLib/t35_tool/injection/MebxMe4cStrategy.cpp +++ b/IsoLib/t35_tool/injection/MebxMe4cStrategy.cpp @@ -268,51 +268,41 @@ MP4Err MebxMe4cStrategy::inject(const InjectionConfig &config, const MetadataMap LOG_DEBUG("Created key_value handle with it35 4CC"); // Build setupInfo with T.35 prefix in binary format: - // 1. utf8string description (null-terminated, '\0' if empty) + // 1. unsigned int(8) t35_identifier_length // 2. unsigned int(8) t35_identifier[] (binary bytes) MP4Handle setupInfo = nullptr; { - const std::string &desc = prefix.description(); - std::vector identifierBytes = prefix.toBytes(); - - // Calculate total size: description length + null terminator + identifier bytes - u32 descLen = desc.empty() ? 1 : (u32)desc.size() + 1; // '\0' if empty, or string + '\0' - u32 totalSize = descLen + (u32)identifierBytes.size(); - - err = MP4NewHandle(totalSize, &setupInfo); + LOG_DEBUG("Creating temporary it35 sample entry"); + MP4T35MetadataSampleEntryPtr it35 = nullptr; + u32 dataReferenceIndex = 0; // unused + err = ISONewT35SampleDescription(&it35, dataReferenceIndex, prefix.toString().c_str()); if(err) { - LOG_ERROR("Failed to create setupInfo handle (err={})", err); - MP4DisposeHandle(key_value); + LOG_ERROR("Failed to create temporary it35 sample description (err={})", err); return err; } - char *buffer = *setupInfo; - u32 offset = 0; - - // Write description as null-terminated UTF-8 string - if(desc.empty()) - { - buffer[offset++] = '\0'; // Just null byte if no description - } - else + err = MP4NewHandle(0, &setupInfo); + if(err) { - memcpy(buffer + offset, desc.c_str(), desc.size()); - offset += desc.size(); - buffer[offset++] = '\0'; // Null terminator + LOG_ERROR("Failed to create setupInfo handle (err={})", err); + MP4DisposeHandle(key_value); + return err; } - - // Write t35_identifier as binary bytes - if(!identifierBytes.empty()) + err = atomPtrToSampleEntryH(setupInfo, (MP4AtomPtr)it35); + if(err) { - memcpy(buffer + offset, identifierBytes.data(), identifierBytes.size()); - offset += identifierBytes.size(); + LOG_ERROR("Failed to convert temporary it35 sample entry to (err={})", err); + MP4DisposeHandle(key_value); + MP4DisposeHandle(setupInfo); + return err; } - - LOG_DEBUG("Created setupInfo handle: description='{}' ({} bytes), identifier={} bytes", - desc.empty() ? "(empty)" : desc, descLen, identifierBytes.size()); } + // Skip the box size and 'it35' type, and the reserved and dataReferenceIndex fields + err = MP4SetHandleOffset(setupInfo, 4 + 4 + 6 + 2); + if(err) return err; + // Add sample entry with me4c namespace // For me4c namespace, desired_local_key_id must match the 4CC in key_value u32 desired_key_id = MP4_FOUR_CHAR_CODE('i', 't', '3', '5'); @@ -324,7 +314,7 @@ MP4Err MebxMe4cStrategy::inject(const InjectionConfig &config, const MetadataMap MP4_FOUR_CHAR_CODE('m', 'e', '4', 'c'), // me4c namespace key_value, // 'it35' 4CC NULL, // locale_string (not used) - setupInfo); // T.35 prefix string + setupInfo); // T.35 prefix string and optional hrsd box MP4DisposeHandle(key_value); MP4DisposeHandle(setupInfo);