|
15 | 15 | #include <fstream> |
16 | 16 | #include <iostream> |
17 | 17 | #include <stdexcept> |
| 18 | +#include <map> |
18 | 19 | #include <vector> |
19 | 20 | #include <limits> |
20 | 21 |
|
@@ -197,6 +198,40 @@ static PreparedTextTracks prepare_text_tracks( |
197 | 198 | return prepared; |
198 | 199 | } |
199 | 200 |
|
| 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 | + |
200 | 235 | static DurationInfo compute_durations(const AacExtractResult &aac, Mp4aConfig &audio_cfg, |
201 | 236 | const std::vector<ChapterTextSample> &text_chapters, |
202 | 237 | const std::vector<ChapterImageSample> &image_chapters) { |
@@ -402,7 +437,8 @@ bool write_mp4(const std::string &output_path, const AacExtractResult &aac, |
402 | 437 | const auto &audio_samples = aac.frames; // reuse extracted buffers without copying |
403 | 438 |
|
404 | 439 | // 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); |
406 | 442 |
|
407 | 443 | // |
408 | 444 | // Image samples: JPEG binary data (may be empty if no images were provided) |
|
0 commit comments