+ // Get all the HLS zombies and combine them into one map (we resolve conflicts
+ // by having an arbitrary element win; in practice, that means the lowest
+ // server ID).
+ for (HLSZombie &zombie : servers->get_hls_zombies()) {
+ const string remote_addr = zombie.remote_addr;
+ remaining_hls_zombies[move(remote_addr)] = move(zombie);
+ }
+
+ // Remove all zombies whose ID match an already ongoing request.
+ // (Normally, this is cleared out already when it starts,
+ // but the request could happen on a different server from the zombie,
+ // or the zombie could be deserialized.)
+ for (const ClientStats &stats : servers->get_client_stats()) {
+ if (stats.url != "-") {
+ remaining_hls_zombies.erase(stats.hls_zombie_key);
+ }
+ }
+
+ for (const ClientStats &stats : servers->get_client_stats()) {
+ string url = stats.url;
+ if (url == "-") {
+ // No download going on currently; could it be waiting for more HLS fragments?
+ auto it = remaining_hls_zombies.find(stats.remote_addr);
+ if (it != remaining_hls_zombies.end()) {
+ url = it->second.url;
+ remaining_hls_zombies.erase(it);
+ }
+ }
+
+ fprintf(fp, "%s %d %d %s %d %llu %llu %llu \"%s\" \"%s\"\n",
+ stats.remote_addr.c_str(),
+ stats.sock,
+ 0, // Used to be fwmark.
+ url.c_str(),
+ int(now.tv_sec - stats.connect_time.tv_sec), // Rather coarse.
+ (long long unsigned)(stats.bytes_sent),
+ (long long unsigned)(stats.bytes_lost),
+ (long long unsigned)(stats.num_loss_events),
+ stats.referer.c_str(),
+ stats.user_agent.c_str());
+ }
+ for (const auto &url_and_zombie : remaining_hls_zombies) {
+ const HLSZombie &zombie = url_and_zombie.second;