Skip to content

Commit 75c5174

Browse files
authored
Merge pull request #11 from tillt/till/fix-urls
fix: URLs now getting exposed by AVFoundation loadChapters
2 parents 3c2d672 + 62703d1 commit 75c5174

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/mp4_muxer.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <fstream>
1616
#include <iostream>
1717
#include <stdexcept>
18+
#include <map>
1819
#include <vector>
1920
#include <limits>
2021

@@ -197,6 +198,40 @@ static PreparedTextTracks prepare_text_tracks(
197198
return prepared;
198199
}
199200

201+
// Mirror href values from any auxiliary text tracks onto the primary title track so that
202+
// players that only inspect the chapter title track (e.g., AVFoundation) still surface URLs.
203+
static std::vector<ChapterTextSample> merge_href_into_titles(
204+
const std::vector<ChapterTextSample> &titles,
205+
const std::vector<std::pair<std::string, std::vector<ChapterTextSample>>> &extra_text_tracks) {
206+
if (titles.empty() || extra_text_tracks.empty()) {
207+
return titles;
208+
}
209+
210+
std::multimap<uint32_t, std::string> href_by_start;
211+
for (const auto &track : extra_text_tracks) {
212+
for (const auto &sample : track.second) {
213+
if (!sample.href.empty()) {
214+
href_by_start.emplace(sample.start_ms, sample.href);
215+
}
216+
}
217+
}
218+
if (href_by_start.empty()) {
219+
return titles;
220+
}
221+
222+
auto merged = titles;
223+
for (auto &t : merged) {
224+
if (!t.href.empty()) {
225+
continue;
226+
}
227+
auto it = href_by_start.find(t.start_ms);
228+
if (it != href_by_start.end()) {
229+
t.href = it->second;
230+
}
231+
}
232+
return merged;
233+
}
234+
200235
static DurationInfo compute_durations(const AacExtractResult &aac, Mp4aConfig &audio_cfg,
201236
const std::vector<ChapterTextSample> &text_chapters,
202237
const std::vector<ChapterImageSample> &image_chapters) {
@@ -402,7 +437,8 @@ bool write_mp4(const std::string &output_path, const AacExtractResult &aac,
402437
const auto &audio_samples = aac.frames; // reuse extracted buffers without copying
403438

404439
// Build padded tx3g samples for title and URL tracks.
405-
PreparedTextTracks prepared_text = prepare_text_tracks(text_chapters, extra_text_tracks);
440+
auto primary_with_href = merge_href_into_titles(text_chapters, extra_text_tracks);
441+
PreparedTextTracks prepared_text = prepare_text_tracks(primary_with_href, extra_text_tracks);
406442

407443
//
408444
// Image samples: JPEG binary data (may be empty if no images were provided)

tests/avfoundation_urltext.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,11 @@ for idx, c in enumerate(chapters):
133133
# Validate hrefs (if any) show up either in sample payload or extraAttributes dump
134134
hrefs = [c.get("url", "") for c in chapters if c.get("url")]
135135
for href in hrefs:
136-
if href not in logtxt:
137-
fail(f"Missing href '{href}' in Swift log")
136+
# Require the href to appear in the chapterMetadataGroups extraAttributes (HREF),
137+
# not just in raw text payload dumps.
138+
pattern = rf"item key=.*extra=.*HREF.*{re.escape(href)}"
139+
if not re.search(pattern, logtxt):
140+
fail(f"Missing href '{href}' in chapterMetadataGroups (extraAttributes)")
138141
139142
print("AVFoundation URL text OK (strings, timings, hrefs present)")
140143
PY

0 commit comments

Comments
 (0)