Commit 1b50f5f5 for libheif
commit 1b50f5f5592561d456e4bd8a3e0ac43539e8f1b7
Author: Rob Smith <rob.smith@awayteam.co.uk>
Date: Fri Feb 20 11:59:43 2026 +0000
Update heif_enc.cc
Added support for WebVMT sync commands
Added support for decoding base64 binary metadata
diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc
index 5bfcd784..c9008205 100644
--- a/examples/heif_enc.cc
+++ b/examples/heif_enc.cc
@@ -2190,6 +2190,83 @@ static std::vector<uint8_t> hex_to_binary(const std::string& line)
}
+// Convert base64 data to raw binary.
+static std::vector<uint8_t> decode_base64(const std::string& line)
+{
+ const std::string base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ std::vector<uint8_t> data;
+
+ size_t len = line.size();
+ if (len % 4 != 0) {
+ len = int(len / 4) * 4;
+ }
+
+ for (size_t i = 0; i < len; i += 4) {
+ uint8_t buf[4];
+ for (uint8_t j = 0; j < 4; j++) {
+ size_t k = base64.find(line[i + j]);
+ buf[j] = (uint8_t)(k == std::string::npos ? base64.size() : k);
+ }
+
+ data.push_back((uint8_t)(buf[0] << 2) + (buf[1] >> 4));
+
+ if (line[i + 2] != '=') {
+ data.push_back((uint8_t)((buf[1] & 0x0f) << 4) + (buf[2] >> 2));
+ }
+
+ if (line[i + 3] != '=') {
+ data.push_back((uint8_t)((buf[2] & 0x03) << 6) + buf[3]);
+ }
+ }
+
+ return data;
+}
+
+
+// Parse metadata from WebVMT sync commmands
+static std::vector<uint8_t> parse_vmt_sync_data(const std::string& content)
+{
+ std::vector<uint8_t> data;
+
+ std::regex pattern_sync(R"(\s*\{\s*\"sync\"\s*:\s*\{(.*?)\}\s*\}\s*)");
+ const std::sregex_token_iterator ti_end;
+
+ for (std::sregex_token_iterator ti(content.begin(), content.end(), pattern_sync, 1); ti != ti_end; ++ti)
+ {
+ std::string sync = *ti;
+ std::regex pattern_type(R"(.*\"type\"\s*:\s*\"(.*?)\".*)");
+ std::regex pattern_data(R"(.*\"data\"\s*:\s*\"(.*?)\".*)");
+ std::smatch match;
+
+ if (std::regex_match(sync, match, pattern_type)) {
+ std::string type = match[1];
+
+ std::regex pattern_hex(R"(.*\.hex$)");
+ std::regex pattern_b64(R"(.*\.base64$)");
+
+ std::string textData;
+ if (std::regex_match(sync, match, pattern_data)) {
+ textData = match[1];
+ }
+
+ if (std::regex_match(type, match, pattern_hex)) {
+ std::vector<uint8_t> binaryData = hex_to_binary(textData);
+ data.insert(data.end(), binaryData.begin(), binaryData.end());
+ }
+ else if (std::regex_match(type, match, pattern_b64)) {
+ std::vector<uint8_t> binaryData = decode_base64(textData);
+ data.insert(data.end(), binaryData.begin(), binaryData.end());
+ }
+ else {
+ data.insert(data.end(), textData.data(), textData.data() + textData.length());
+ }
+ }
+ }
+
+ return data;
+}
+
+
uint32_t parse_vmt_timestamp(const std::string& vmt_time)
{
std::regex pattern(R"(-?((\d*):)?(\d\d):(\d\d)(\.(\d*))?)");
@@ -2298,6 +2375,8 @@ int encode_vmt_metadata_track(heif_context* context, heif_track* visual_track,
}
concat.push_back(0);
+ std::string content(concat.begin(), concat.end());
+ concat = parse_vmt_sync_data(content);
}
if (ts != BAD_VMT_TIMESTAMP) {
@@ -2311,14 +2390,14 @@ int encode_vmt_metadata_track(heif_context* context, heif_track* visual_track,
concat.insert(concat.begin(), prev_metadata.begin(), prev_metadata.end());
}
else {
- std::cerr << "Bad VMT timestamp order: " << cue_start << "\n";
+ std::cerr << "Bad WebVMT timestamp order: " << cue_start << "\n";
}
prev_ts = ts;
prev_metadata = concat;
}
else {
- std::cerr << "Bad VMT timestamp: " << cue_start << "\n";
+ std::cerr << "Bad WebVMT timestamp: " << cue_start << "\n";
}
}